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
    • Logic Bugs: Off-by-One, Width and Sign
    • FSM Bugs
    • X and Reset Bugs
    • Races and Simulation-Synthesis Mismatches
    • Latches and Combinational Loops
    • CDC Bugs
    • Reset-Domain Crossing Bugs
    • Timing-Only Bugs
    • Protocol Violations
    • Hangs and Deadlocks
    • Starvation and Livelock
    • Performance Bugs
    • Power-Intent Bugs
    • DFT-Mode Bugs
    • Configuration and Register-Map Bugs
    • Testbench Bugs That Look Like Design Bugs
  • 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
Loading lesson

‹Part 5 · Bug Classes
  1. Topics
  2. The Art of Hardware Debug
  3. Part 5 · Bug Classes
  4. DFT-Mode Bugs

Part 5 · Bug Classes · Senior (S)

DFT-Mode Bugs

Test logic that leaks into functional mode: a scan enable left on, test mode reaching the function, and clock or reset muxes that pick the test path. Their signatures, the first three checks, a small block with two test-glue bugs in one self-checking bench, and the functional-mode check that catches them at the first cycle.

On this page

    Why this matters

    Test logic is meant to be invisible in functional mode, so a leak looks like a random functional bug: a register that changes with nobody writing it, or one that ignores reset. Without the class in mind it can take days, and the scan muxes that cause it may not even exist in the RTL you are simulating.

    After this lesson you can: recognise a scan-enable or test-mode leak and a wrong test reset or clock mux from the shape of the wrong values, trace each test control to its source, and add the functional-mode check that catches it at once.

    Note

    Level: Senior (S) · Debug-loop step: Localise, Hypothesise

    Before this: The First Wrong Cycle, Cone-of-Influence Tracing

    The register that shifts when an LED blinks

    Firmware reports that a control register changes by itself. It is written once at boot with 5b, and sometimes reads d8 later. Nobody writes it again; the bus monitor agrees. The firmware team notices it happens after the code that blinks a status LED. The RTL of the register is four lines and looks right. What would make a register change with no write?

    What people usually do, and why it fails

    The usual hunt starts at the register's write enable and the bus decoder, because those are the only paths into the register in the RTL. They are clean, so the next suspects are glitches, timing and "cosmic rays". All of these miss the paths that are not in the functional RTL: the scan mux on every flop, the test-mode reset and clock muxes, and the pads that are shared between test pins and functional pins.

    Test logic gets less review than the function because it is "only for the tester". It is still logic, and in functional mode its rule is simple: every test control is constant and inactive. A bug in this class breaks that rule.

    The signature

    Sub-class

    What you see

    Where it shows

    Scan enable leaks into function

    a register holds its old value shifted by N bits (N cycles of shift), new bits from the neighbour flop or scan-in; changes track unrelated pin activity

    gate-level simulation of the scan netlist; RTL only where the shift path is modelled; silicon

    Test mode reaches the function

    clock gates forced open, memories in bypass, resets or clocks from the test path; often power or behaviour that differs board to board

    any simulation that drives the test-mode source; a strap or pad with the wrong default

    Wrong test reset or clock mux

    some flops ignore a functional reset, or run on the test clock; a subset of state survives a reset

    RTL, when the mux is hand-written glue; otherwise gate level

    Here is the first row as a waveform: functional mode throughout, and software drives a GPIO that shares its pad with scan enable. cfg_good has the correct glue; cfg_bug takes scan enable straight from the pad.

    Read the data as a bit pattern, not as a number.

    • Slot 1: both registers hold 5b, written by software. test_mode is 0 in every slot.

    • Slots 3 to 5: the GPIO is high, and so is the leaked se_bug. Each cycle cfg_bug moves one bit left: 5b, b6, 6c, d8. A 0 enters from scan-in.

    • Slot 6: the GPIO goes low and cfg_bug stops changing. It changes only while the pin is high, which is the clue that points away from the bus.

    The first three checks

    On the debug loop this is Localise followed quickly by Hypothesise: the shape of the wrong value names the sub-class.

    1. Print every test control in the failing functional run. test_mode, every scan enable, test clock and reset selects, memory bypass. In functional mode each must be constant and inactive; any toggle, any 1, or an X is the bug or leads to it.

    2. Read the wrong value as bits. Old value shifted by N bits means a scan shift. A value that survives a functional reset means the reset mux. Behaviour at the wrong rate means a clock mux. A forced-open clock gate shows as activity with no enable.

    3. Trace each test control to its source. A pad, a strap or a test controller register. Who drives it in functional mode, what is its reset value, and is it qualified by test_mode before it fans out?

    One real reproduction: two bugs in the test glue

    The block below is an 8-bit configuration register with its test glue. The scan-enable pad doubles as a GPIO in functional mode, and in test mode the flops take their reset from the tester's reset pin. BUG = 1 uses the pad as scan enable without the test_mode qualifier; BUG = 2 has the reset mux's inputs swapped. The bench runs all three in functional mode, blinks the GPIO, applies a functional reset, then checks that the correct glue still shifts in test mode. A small always-on check states the functional-mode rule.

    dft_blk.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    // dft_blk: an 8-bit configuration register as it behaves after scan insertion, with its test glue.
    // The scan mux (se ? shift : function) is what insertion adds to every flop; it is modelled here so
    // the bug shows in RTL simulation. In a real flow it shows in a simulation of the scan netlist.
    // Test glue: the scan-enable pad is shared with a GPIO in functional mode, and in test mode the
    // flops take their reset from the tester's reset pin.
    //   BUG = 0: correct glue.
    //   BUG = 1: scan enable is taken from the shared pad without the test_mode qualifier.
    //   BUG = 2: the reset mux's inputs are swapped.
    `timescale 1ns/1ps
    module dft_blk #(parameter int BUG = 0) (
      input  logic       clk, rst_n, test_mode, se_pad, si, tst_rst_n, wr,
      input  logic [7:0] wdata,
      output logic [7:0] cfg_q,
      output logic       so
    );
      logic se, rst_int_n;
      assign se        = (BUG == 1) ? se_pad : (test_mode && se_pad);
      assign rst_int_n = (BUG == 2) ? (test_mode ? rst_n : tst_rst_n)
                                    : (test_mode ? tst_rst_n : rst_n);
      always_ff @(posedge clk or negedge rst_int_n)
        if (!rst_int_n) cfg_q <= 8'h00;
        else if (se)    cfg_q <= {cfg_q[6:0], si};   // scan shift: inserted by DFT
        else if (wr)    cfg_q <= wdata;              // function
      assign so = cfg_q[7];
    endmodule
    
    // dft_mode_check: the functional-mode rule as an always-on check. While test_mode is low, scan
    // enable must be low and the flops' reset must be the functional reset.
    module dft_mode_check (
      input logic clk, test_mode, se, rst_n, rst_int_n
    );
      int    cycle = 0;
      string first = "";
      always @(posedge clk) begin                  // the bench changes pins on the falling edge
        if (!test_mode && first == "" && (se !== 1'b0 || rst_int_n !== rst_n))
          first = $sformatf("cycle %0d: se=%b rst_n=%b rst_int_n=%b with test_mode=0", cycle, se, rst_n, rst_int_n);
        cycle++;
      end
    endmodule
    
    Terminal
    $ iverilog -g2012 -o dft.vvp dft_tb.sv && vvp -n dft.vvp
    BUG=0 (correct glue):          clean
    BUG=1 (scan enable from pad):  after 3 GPIO-high cycles cfg_q=d8, expected 5b
    BUG=2 (reset mux swapped):     after a functional reset cfg_q=5b, expected 00
    mode check, BUG=0:             clean
    mode check, BUG=1:             cycle 5: se=1 rst_n=1 rst_int_n=1 with test_mode=0
    mode check, BUG=2:             cycle 0: se=0 rst_n=0 rst_int_n=1 with test_mode=0
    test mode on the correct glue: shifted in a1, chain holds a1
    PASS: both test-glue bugs fail in functional mode; the correct glue passes and still shifts
    ...

    Verilator 5.048 prints the same eight lines. Read them in pairs.

    • BUG=1. The data check sees d8 after three GPIO cycles: 5b shifted left by three, exactly the waveform. The mode check fires at cycle 5, the first edge with the pin high, with se=1 in functional mode. That line names the cause; the data line only names the victim.

    • BUG=2. The data check needs a mid-test reset to notice: the register keeps 5b. The mode check fires at cycle 0, during the power-on reset: rst_n=0 but the flops see rst_int_n=1. The functional reset never reached them, not even at boot. The bench's data checks missed that, because software wrote the register before reading it.

    • The test-mode line matters for the fix: whatever you change in the glue, the chain must still shift a1 through in test mode.

    Where this shows in a real flow

    Here the scan mux is in the RTL so that the bug can be simulated. In a real design scan insertion adds it to the netlist, so the bug shows only at gate level, in the DFT tool's rule checks or on the tester; Part 6 covers moving between those stages. dbglab has no scan logic, so none of its bugs belongs to this class.

    The fix pattern

    • Qualify every test control with `test_mode` once, at its source, and fan out the qualified signal: se = test_mode && se_pad. Never use a shared pad directly.

    • Select test muxes with the static `test_mode`, never with a dynamic scan enable, and check which input is which against the spec: in functional mode, the functional reset and clock.

    • Give the test controller and any straps a reset value that means functional mode, so a floating or unprogrammed source cannot start the chip in test mode.

    Preventing it

    • A functional-mode assertion in every functional test: while test_mode is low, scan enables are 0 and test muxes pass the functional input. dft_mode_check is that assertion; it found both bugs at their first cycle, one of them during boot.

    • Drive shared pads in functional tests. A bench that never toggles the GPIO will never see the leak. Toggle every pad that doubles as a test pin.

    • DFT rule checks and a scan-netlist simulation before sign-off, and a test-mode test that still passes after any glue change, like the a1 shift above.

    • The review question: in functional mode, what drives this test pin, and can it ever be 1? See the test-mode integration checklist.

    Traps

    Common pitfall

    Fixing the victim. Guarding the register's write path or adding a parity check on it hides one symptom; every other flop on the chain still shifts. Fix the control at its source.

    Common pitfall

    Disabling checks in test mode, then leaking test mode. Assertions are often written disable iff (test_mode) so they stay quiet on the tester. If test_mode leaks into function, the same line switches the checks off exactly when you need them. Check test_mode itself in every functional test.

    Why this matters

    Senior lens. When a functional failure appears only on some boards, or only after an unrelated pin toggles, put test logic on the suspect list early. Pads shared between test and function, and straps with weak defaults, are the usual paths.

    Ask DFT and RTL to own the functional-mode rule together: DFT writes the list of test controls, RTL proves each one is qualified, and DV runs the functional-mode check in every test. A leak is a hand-off bug more often than a coding error.

    Interview

    Asked: "A configuration register changes value in functional mode without any bus write. How do you debug it?"

    Weak answer: I would add a monitor on the register's write enable and look for glitches on the bus, and maybe check the timing reports for that path.

    Strong answer: I would first look at the wrong value as bits: if it is the old value shifted by a few positions, that is a scan shift and I would check the scan enable. In any case I would print every test control in the failing run, because in functional mode each must be constant and inactive. Then I would trace the one that moves to its source, often a pad shared with a GPIO or a test-controller register, and check that it is qualified by test mode. The fix goes at the source, and I would add an always-on functional-mode assertion so the next leak fails at its first cycle.

    Follow-ups: (1) Why might this bug never show in RTL simulation? (2) How would a swapped reset mux show up?

    Check yourself After a functional reset, three registers in one block keep their old values and the rest clear. Writes and reads work normally. Which sub-class, and what do you print first?

    A test reset mux (or a reset bypass) that selects the test reset in functional mode, for the flops behind that mux. Print test_mode, the mux select and the reset each flop actually sees around the reset pulse, as the mode check does with rst_n and rst_int_n. BUG=2 showed the same thing: the flops never saw the functional reset, not even at boot.

    Check yourself A colleague fixes BUG=1 by tying the scan-enable pad low on the board. Is the chip fixed?

    No. The board change hides the leak on that board only; the GPIO function is lost, and any board or package that drives the pad still shifts the chain. The fix is in the chip: qualify scan enable with test_mode at its source, check that the test-mode shift still works, and keep the functional-mode check in the regression.

    Remember

    • A register holding its old value shifted by N bits, after N cycles of unrelated pin activity, is a scan shift: look for an ungated scan enable.

    • Flops that ignore a functional reset, or run at the wrong clock, point at a test mux that picked the test input.

    • Scan muxes are added at insertion, so the RTL may have no path for the bug: it shows at gate level, in DFT rule checks or on the tester.

    Rule of thumb: in functional mode every test control is constant and inactive: check that before you debug the data.

    How this was checked

    dft_tb.sv as shown, 2026-10-18: iverilog -g2012 -o dft.vvp dft_tb.sv && vvp -n dft.vvp (Icarus Verilog 13.0) and verilator --binary --timing --assert -Wno-fatal --top-module dft_tb -Mdir vl dft_tb.sv && ./vl/Vdft_tb (Verilator 5.048) print the same eight lines shown and PASS. The waveform is generated from wave-src/dft-mode-bugs/se-leak.sv with COURSE=dbg node scripts/tools/gen-waves.mjs bug-classes:dft-mode-bugs.

    Limits: the scan mux is modelled in RTL; real scan insertion adds it to the netlist, so in a real flow this class shows in gate-level simulation of the scan-inserted netlist, in DFT rule checks, or on the tester. No DFT tool, ATPG or netlist was run. dbglab has no scan logic, so none of its five bugs is in this class.

    Keep going

    • Practice: Disabling an assertion during scan shift

    • Practice: Disabling assertions based on a test-mode signal

    • Code lab: Scan chain stub

    • Code lab: DFT scan enable

    • Interview: run a mock interview on this topic

    Related topics

    • Part 5 · Bug Classes

    • RTL: a scan-chain model at RTL

    • RTL: test clocks, resets and modes

    • RTL: scan-ready coding

    • RTL: DFT and scan

    • Part 10: case studies, including the scan-mode leak

    Previous

    53 / 107

    Next: Configuration and Register-Map Bugs

    Up next

    Up nextConfiguration and Register-Map Bugs

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Logic Bugs: Off-by-One, Width and Sign
    • FSM Bugs
    • X and Reset Bugs
    test_modegpiose_bugcfg_good05bcfg_bug05bb66cd8
    A GPIO that shares the scan-enable pad is high for three cycles in functional mode: the leaked scan enable shifts the register from 5b to b6, 6c and d8.