DFT · Chapter 14 · Industry Case Studies
Case Study — FSM: ATPG & Coverage Closure
The third capstone case takes the project thread's FSM through ATPG and coverage closure, adding branching next-state logic on top of the counter's linear feedback. An FSM is state flops plus branching next-state logic and output logic, a richer combinational cone than a counter, so it has more faults and more corners such as unreachable states and default branches. With scan inserted and DRC-clean, ATPG treats the FSM as combinational between scan cells: load any state by shifting, apply inputs, capture one clock, and observe, so it can jump to any state, even unreachable ones. That is why scan plus ATPG beats functional test for defects. Coverage closure then analyzes the gap, whose usual sources are redundant default-branch logic, uninitialized X-sources, and unobservable outputs, closed by test points, masking, or classifying redundant faults. Report both test and fault coverage, and do not chase full coverage blindly.
Intermediate15 min readDFTCase StudyFSMATPGCoverage Closure
Chapter 14 · Section 14.3 · Industry Case Studies
Project thread — the FSM, ATPG-closed: the counter's linear feedback (14.2) now branches. Scan makes it a combinational test; 14.4 adds test control for clock-gating; 14.6 scales to an IP.
1. Why Should I Learn This?
The FSM case shows the defining power of scan + ATPG: it turns a sequentially-deep state machine into a combinational test problem — high coverage, few patterns, any state — and it shows how to close the coverage gap honestly.
- An FSM = flops + branching next-state logic → more faults/corners than a counter (unreachable states, default branches).
- Scan makes it combinational between cells: ATPG jumps to any state (even unreachable), applies inputs, captures next-state, observes.
- Coverage closure = gap analysis: unreachable/redundant/X/unobservable → test points, mask X, classify redundant, fix RTL.
- Report test and fault coverage (6.x); don't chase 100% blindly — default/illegal-state logic is the classic redundant source.
2. Real Silicon Story — the coverage that stalled on a dead branch
An FSM block's fault coverage stalled at ~92% against a 98% target. The team added patterns, re-ran ATPG, and chased the number for days — coverage wouldn't budge. A respin of the FSM was floated to "improve testability."
The gap was not a hole — it was redundant logic. Analyzing the undetected faults, they clustered in the next-state default branch — the transition logic for an illegal/unreachable state. ATPG had correctly reported those faults as AU/redundant (5.x/6.x): the branch can't be sensitized because the state is unreachable and its inputs are don't-cares, so no pattern can excite and propagate a fault there — the faults are legitimately untestable, not missed. The fix was classification, not more patterns: classify the redundant faults (so fault coverage reflects that they're untestable) and add an observe point on the state where it did add value. The reported coverage then reflected reality — the test coverage (of testable faults) had been high all along; only the fault coverage was dragged down by redundant faults counted against it. Lesson: scan + ATPG makes an FSM a combinational test that reaches any state — but coverage closure means analyzing the gap, and the classic FSM gap is redundant default/illegal-state logic. Report test and fault coverage, classify redundant faults, and don't chase 100% blindly.
3. Factory Perspective — the FSM case through each lens
- What the DFT/ATPG engineer sees: ATPG treating the FSM as combinational — load state, apply inputs, capture, observe — and the coverage gap to analyze and close.
- What the RTL/DV engineer sees: the redundant default/illegal-state logic, uninit output regs (X), and unobservable outputs — the RTL sources of the gap (fix or accept).
- What the STA engineer sees: the same scan-mux setup / shift hold as before (12.x) — the FSM adds branching cone depth, not new timing modes.
- What management cares about: that coverage closes to a real number (not a chased 100%) — signoff on fault + test coverage with redundant faults classified, not a respin for a non-hole.
4. Concept — FSM as combinational test, and closing the gap
The FSM (flops + branching):
- State flops + branching next-state logic (function of state + inputs) + output logic.
- Richer cone than a counter → more faults, more corners: unreachable/illegal states, dead branches, defaults.
Scan makes it combinational (the key insight):
- With scan (inserted + DRC-clean, 14.2), ATPG treats the FSM as combinational between scan cells:
- Load a state (shift) → apply inputs → capture one clock (next-state computes) → observe (shift out).
- ATPG doesn't traverse the state graph — it JUMPS to any state, including 'unreachable' ones, and tests the branch logic directly.
- Scan turns a sequentially-deep FSM into a combinational test problem → high coverage, few patterns, any state. This is why scan + ATPG beats functional test for defects.
Coverage closure (Ch6) — analyze the gap:
- Run ATPG → read fault coverage → analyze undetected faults. Typical FSM gaps:
- (a) Unreachable/illegal states' logic — physically present; ATPG can load & test them (coverage often good), or classify untestable if truly redundant.
- (b) Redundant logic — default branches that can't be sensitized → AU/redundant (5.x/6.x).
- (c) X-sources — uninit output regs → mask.
- (d) Unobservable outputs → add observe/test points (6.x).
- Close: add test points, mask X, classify redundant, or fix RTL.
Test vs fault coverage (6.x):
- Test coverage (of testable faults) can be high while fault coverage (of all faults) trails because of redundant/untestable faults.
- The FSM's default/illegal-state logic is the classic source. Report both; don't chase 100% blindly.
5. Mental Model — testing every room by teleporting, not walking
Scan + ATPG on an FSM is like inspecting a mansion by teleporting into every room — including sealed rooms — instead of walking the hallways in order.
- Functional test is a person walking the mansion: to reach a far room (a deep state) they must traverse every hallway and door (clock through the state sequence) — slow, and some rooms (unreachable states) have no door at all from the inside, so they're never inspected.
- Scan is a teleporter: ATPG drops the inspector directly into any room (load a state), has them flip the switches (apply inputs), watch what happens for one moment (capture next-state), and teleport the result out (observe) — every room inspected, including the sealed ones functional walking could never enter.
- Coverage closure is the inspection report: most rooms pass; the few that don't are usually sealed rooms whose switches connect to nothing (redundant default-branch logic) — not a failure to inspect, but a room with nothing testable in it. You note it as 'sealed/empty' (classify redundant), or you add a window (an observe point) where it helps — you don't demolish the mansion (respin) over an empty sealed room.
- And you report two numbers: rooms-with-something-testable that passed (test coverage) vs all rooms including the empty sealed ones (fault coverage) — so 100% of the real rooms can pass even if the grand total never hits 100%.
Teleport into every room (scan jumps to any state) — even sealed ones — and report which had something to inspect; an empty sealed room (redundant branch) is noted, not demolished.
6. Working Example — the RTL: an FSM with a default branch
Ship a small FSM (arbiter/traffic style) with a default branch, then the scan test intent:
// SystemVerilog - a small FSM with a DEFAULT (illegal-state) branch (REPRESENTATIVE)
module fsm3 (
input logic clk, rst_n, in,
input logic scan_enable, si, // scan controls (chain inserted per 14.2)
output logic grant
);
typedef enum logic [1:0] {S0=2'b00, S1=2'b01, S2=2'b10} state_e;
state_e st, nxt;
always_comb begin
nxt = st; // default hold
unique case (st)
S0: nxt = in ? S1 : S0;
S1: nxt = in ? S2 : S1;
S2: nxt = in ? S0 : S2;
default: nxt = S0; // ILLEGAL-state recovery -> REDUNDANT (unreachable) -> AU faults
endcase
end
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) st <= S0; else st <= nxt; // (scan-stitched flops per 14.2; SE selects SI in shift)
assign grant = (st == S2);
endmodule// Verilog-2001 - FSM with a default branch (REPRESENTATIVE)
module fsm3 (clk, rst_n, in, grant);
input clk, rst_n, in; output grant;
reg [1:0] st, nxt;
localparam S0=2'b00, S1=2'b01, S2=2'b10;
always @(*) begin
case (st)
S0: nxt = in ? S1 : S0;
S1: nxt = in ? S2 : S1;
S2: nxt = in ? S0 : S2;
default: nxt = S0; // redundant (unreachable) -> AU
endcase
end
always @(posedge clk or negedge rst_n)
if (!rst_n) st <= S0; else st <= nxt;
assign grant = (st == S2);
endmodule-- VHDL - FSM with a default branch (REPRESENTATIVE)
library ieee; use ieee.std_logic_1164.all;
entity fsm3 is
port ( clk, rst_n, in_i : in std_logic; grant : out std_logic );
end entity;
architecture rtl of fsm3 is
type state_e is (S0, S1, S2);
signal st, nxt : state_e;
begin
process (st, in_i) begin
case st is
when S0 => if in_i='1' then nxt<=S1; else nxt<=S0; end if;
when S1 => if in_i='1' then nxt<=S2; else nxt<=S1; end if;
when S2 => if in_i='1' then nxt<=S0; else nxt<=S2; end if;
when others => nxt <= S0; -- redundant recovery -> AU
end case;
end process;
process (clk) begin if rising_edge(clk) then st <= nxt; end if; end process;
grant <= '1' when st = S2 else '0';
end architecture;# FSM scan test intent + coverage closure - REPRESENTATIVE, tool-neutral:
SCAN (from 14.2): the state flops are in the chain -> ATPG treats the FSM as COMBINATIONAL between cells
PER-TRANSITION TEST: load state S -> apply input 'in' -> capture ONE clock (nxt computed) -> shift out, observe next-state+grant
(ATPG can LOAD even an illegal/unreachable state SX directly - functional test never could)
COVERAGE CLOSURE:
run ATPG -> fault coverage 92% (below 98% target) -> ANALYZE the undetected faults
-> they cluster in the DEFAULT branch (illegal-state recovery) -> ATPG: AU / REDUNDANT (can't be sensitized)
-> CLASSIFY redundant (not a hole) ; add an observe point where useful ; mask any X from uninit output regs
-> report TEST coverage (of testable faults, already high) AND FAULT coverage (of all, now reflects redundancy)
RULE: don't chase 100% blindly - the default/illegal-state logic is legitimately redundant/untestable.Coverage gaps come from a few identifiable sources, each with its own fix:
7. Industry Flow — scan-test the FSM, then close coverage
The FSM flow: ATPG treats it combinationally (load/apply/capture/observe), then coverage closure analyzes and closes the gap:
8. Debugging Session — coverage stalls on the default branch
FSM fault coverage stalls at about 92 percent against a 98 percent target and the team adds patterns and re-runs ATPG for days with no movement and even floats an FSM respin, but analysis shows the undetected faults cluster in the next-state default branch for an illegal or unreachable state whose inputs are don't-cares so ATPG reports them AU or redundant because they cannot be sensitized -- the fix is to classify the redundant faults so fault coverage reflects reality and add an observe point where useful, after which coverage closes because test coverage of testable faults was already high
A COVERAGE GAP IS OFTEN REDUNDANT DEFAULT-BRANCH LOGIC, NOT A HOLE — ANALYZE AND CLASSIFY; DON'T CHASE 100% BLINDLYFSM fault coverage stalls at ~92% against a 98% target. The team adds patterns and re-runs ATPG for days with no movement, and even floats an FSM respin to "improve testability." Hole, or not?
The undetected faults cluster in the next-state default branch for an illegal or unreachable state, whose inputs are don't-cares so ATPG cannot sensitize them and reports them AU or redundant — it is legitimately untestable logic, not a coverage hole, so adding patterns will never move it. ATPG detects a fault only if it can excite it (force a value at the site) and propagate it to an observable point. For the default branch — the recovery transition of an illegal/unreachable state — neither is possible: the state is never entered functionally, its branch's controlling inputs are effectively don't-cares, and the logic is redundant (its output doesn't change the FSM's observable behavior). So ATPG correctly classifies those faults AU (ATPG-untestable) / redundant (5.x/6.x) — they cannot be detected by any pattern, which is why adding patterns does nothing. This is the classic FSM coverage gap, and it's exactly where the test-vs-fault-coverage distinction matters (6.x): the test coverage (over the testable fault population) was already high — near target — but the fault coverage (over all faults, including the redundant ones) is dragged down because those untestable faults are counted in the denominator. Chasing the number with more patterns, or respinning the FSM, is wasted effort against a non-hole — the logic is doing its safety job (illegal-state recovery) and is inherently redundant by design.
Analyze and classify, don't chase: confirm the undetected faults are the redundant default-branch logic, classify them as redundant/untestable so fault coverage reflects reality, add an observe point where it genuinely helps, and report both test and fault coverage — after which coverage closes because the testable population was already covered. Run the gap analysis: pull the undetected fault list, confirm it clusters in the default/illegal-state branch, and verify ATPG's AU/redundant classification (the branch can't be sensitized). Then take the right action per source: classify the redundant faults (so they're excluded from the achievable-coverage denominator or reported as redundant), add an observe point on the state only where it exposes genuinely testable logic (don't add test points to chase redundant faults — that's wasted area), and mask any X from uninitialized output regs if present. Report both numbers: test coverage (of testable faults — already high, at/above target) and fault coverage (of all faults — now shown with redundant faults classified). Coverage closes to a real number, and no respin is needed. The principle to lock in: scan turns a sequentially-deep FSM into a combinational test problem, so ATPG reaches every branch and even loads unreachable states directly — which means a coverage gap is rarely a failure to reach logic and usually a property of the logic itself: redundant default or illegal-state-recovery branches that cannot be sensitized are reported AU/redundant and are legitimately untestable, not holes, so you close coverage by analyzing the gap and taking the right action per source (classify redundant faults, mask X-sources, add observe points only for genuinely testable-but-unobservable logic, or fix RTL), and you always report test coverage (of the testable population) alongside fault coverage (of all faults) because the two diverge exactly on redundant/untestable faults — chasing 100% fault coverage blindly, or respinning to 'improve testability' against redundant logic, is wasted effort, whereas classifying the redundant faults makes the number reflect reality. (Scan/ATPG combinational treatment is Ch5; AU/redundant classification and test-vs-fault coverage are 5.x/6.x; test points/observability are 6.x; the scan chain is 14.2.)
9. Common Mistakes
- Chasing 100% fault coverage blindly. Redundant/untestable faults (default/illegal-state logic) can't be detected — classify, don't chase.
- Confusing test and fault coverage. Test coverage (testable) can be high while fault coverage (all) trails — report both (6.x).
- Respinning for a non-hole. A default-branch gap is redundant — a classification, not a respin.
- Adding test points for redundant faults. Test points help testable-but-unobservable logic — not redundant logic (wasted area).
- Leaving output regs uninitialized. Uninit output regs → X at compare points — mask or init (13.3).
10. Industry Best Practices
- Use scan + ATPG to treat the FSM combinationally — load any state, apply inputs, capture, observe (reaches unreachable states).
- Close coverage by gap analysis — identify unreachable / redundant / X / unobservable and take the right action per source.
- Classify redundant/untestable faults so fault coverage reflects reality; report test and fault coverage (6.x).
- Add observe/test points only for genuinely testable-but-unobservable logic — not redundant faults.
- Don't chase 100% blindly — a real number with classified redundancy is signoff, a chased 100% is waste.
11. Senior Engineer Thinking
- Beginner: "Coverage is stuck at 92% — add more patterns, or respin to make it testable."
- Senior: "Stuck despite more patterns means the gap is redundant, not missed. I analyze the undetected faults — they cluster in the default/illegal-state branch, which ATPG can't sensitize → AU/redundant. I classify them (so fault coverage reflects reality) and add an observe point only where testable logic needs it. Test coverage was already high. Report both numbers; don't chase 100% blindly. Scan already made this a combinational test reaching every state — the number just needs to tell the truth."
The senior analyzes the gap by source, classifies redundant faults, and reports test + fault coverage rather than chasing a blind 100%.
12. Silicon Impact
The FSM case demonstrates the single most important reason DFT exists in its modern form: scan + ATPG turns a sequentially-deep FSM into a combinational test problem. An FSM is state flops + branching next-state logic + output logic — a far richer combinational cone than the counter's linear increment, with more faults and more corners (unreachable/illegal states, dead branches, defaults). Functionally, testing it is brutal: reaching a deep state may take a long input sequence, and unreachable states can't be reached at all. Scan eliminates the problem: with the chain inserted and DRC-clean (14.2), ATPG treats the FSM as combinational between scan cells — load any state via shift (even an unreachable one), apply inputs, capture one clock (the next-state logic computes), and observe — achieving high coverage with few patterns and reaching states functional test never could. That is why scan + ATPG beats functional test for manufacturing defects. The other half of the case is coverage closure as a discipline of honesty. Running ATPG yields a fault-coverage number, but the number is only as meaningful as its analysis: the gap must be decomposed by source — unreachable/illegal-state logic (ATPG can often load and test it, or classify it untestable), redundant default branches (can't be sensitized → AU/redundant), X-sources (uninit output regs → mask), and unobservable outputs (→ test points) — each closed with the right action (classify / mask / test point / RTL), not by blindly adding patterns. The test-vs-fault-coverage distinction (6.x) is the case's sharpest lesson: test coverage (over testable faults) can be at target while fault coverage (over all faults) trails, because redundant/untestable faults — classically the FSM's default/illegal-state logic — sit in the denominator; the remedy is to classify them so the number reflects reality, and to report both. Chasing 100% fault coverage blindly, or respinning to "improve testability" against redundant logic, is wasted effort against a non-hole. For the DFT/ATPG engineer, this case is combinational treatment + gap analysis; for the RTL/DV engineer, it's where redundancy, X-init, and observability come from; for signoff, it's closing on a real number with redundancy classified; and for the program, it's the proof that coverage is a bounded escape-risk metric, analyzed — not a quality score, chased. The FSM is the counter with branching; the next case (14.4) adds test control for clock-gated logic, and the arc culminates in an IP (14.6) where these same principles run at scale with compression. The throughline: scan makes any state testable, ATPG makes it combinational, and coverage closure is analysis and classification — not a number to chase.
13. Engineering Checklist
- Used scan + ATPG combinationally — load state, apply inputs, capture, observe (reaches unreachable states).
- Ran coverage and analyzed the gap by source — unreachable / redundant / X / unobservable.
- Classified redundant/untestable faults (default/illegal-state logic) so fault coverage reflects reality.
- Added observe/test points only for genuinely testable-but-unobservable logic; masked X from uninit output regs.
- Reported test and fault coverage (6.x) — signed off on a real number, didn't chase a blind 100%.
14. Try Yourself
- Explain why an FSM's branching makes functional test hard — and how scan makes it a combinational test.
- Show the per-transition test via scan: load state → apply inputs → capture (next-state) → observe — including an unreachable state.
- List the coverage-gap sources (unreachable / redundant / X / unobservable) and the right fix for each.
- Explain test vs fault coverage (6.x) and why an FSM's default-branch logic makes them diverge.
- Debug a coverage stall — why more patterns won't move redundant faults, and what you do instead (classify/observe).
The ATPG/coverage reasoning is tool-neutral; patterns/coverage are ATPG outputs. No paid tool required to reason about the FSM case.
15. Interview Perspective
- Weak: "You run ATPG on the FSM and try to get coverage up."
- Good: "Scan lets ATPG test the FSM combinationally by loading states; coverage gaps often come from redundant logic you classify."
- Senior: "The FSM's branching and deep state space make functional test incomplete, so scan turns it into a combinational test — ATPG loads any state (even unreachable ones), applies inputs, captures next-state, and observes: high coverage, few patterns. Then coverage closure is gap analysis, not number-chasing: I decompose the undetected faults into unreachable / redundant-default / X / unobservable and take the right action — classify redundant (default/illegal-state logic is AU, legitimately untestable), mask X, add observe points only for testable logic. And I report test and fault coverage (6.x) — they diverge on redundant faults, so a 92% fault coverage can be a near-perfect test coverage once redundancy is classified. Don't chase 100% blindly."
16. Interview / Review Questions
17. Key Takeaways
- An FSM = state flops + branching next-state logic + output logic — a richer cone than a counter, with more faults/corners (unreachable/illegal states, dead branches, defaults).
- Scan turns the FSM into a combinational test (14.2): ATPG loads any state (even unreachable), applies inputs, captures one clock, observes — high coverage, few patterns, any state. This is why scan + ATPG beats functional test for defects.
- Coverage closure = gap analysis by source: unreachable/illegal-state (load & test, or classify), redundant default branches (AU → classify), X-sources (mask), unobservable outputs (test points) — take the right action, don't add patterns blindly.
- Report test and fault coverage (6.x): test coverage (testable) can be high while fault coverage (all) trails on redundant/untestable faults — the default/illegal-state logic is the classic source; classify it so the number reflects reality.
- Don't chase 100% blindly / don't respin for a non-hole — a coverage stall is usually redundant logic, a classification not a respin. Next: 14.4 — clock-gated block: test control.
18. Quick Revision
Case study — FSM: ATPG & coverage closure. FSM = flops + BRANCHING next-state + output logic → richer cone than a counter (unreachable states, dead branches, defaults). SCAN (14.2) makes it COMBINATIONAL between cells: ATPG LOADS any state (even unreachable) → APPLY inputs → CAPTURE one clock (next-state) → OBSERVE. No functional traversal → high coverage, few patterns → why scan+ATPG beats functional test for defects. COVERAGE CLOSURE = analyze the gap by source: unreachable/illegal-state (load&test or classify) · REDUNDANT default branch (can't sensitize → AU → CLASSIFY, not a hole) · X-source (uninit output reg → MASK) · unobservable (→ test point). TEST vs FAULT coverage (6.x): test coverage (testable) high while fault coverage (all) trails on redundant faults → report BOTH, don't chase 100% blindly. DEBUG: coverage stalls → it's the default/illegal-state branch (AU/redundant) → classify, don't respin. Ships RTL (SV/Verilog/VHDL). Next: 14.4 — clock-gated block: test control.