Skip to content

GLS · Chapter 5 · GLS Testbench Adaptation

Working Example: Adapting an FSM Testbench

This capstone brings the whole chapter together on a new design: a small finite state machine, the natural next step after the counter. Starting from the FSM RTL in SystemVerilog, Verilog, and VHDL, it synthesizes to a netlist and adapts the original RTL testbench that breaks on it. The fragile internal state reference becomes port-based and bind-based checking against a preserved signal, stimulus is driven a skew after the clock edge clear of the timing window, outputs are sampled on an unknown-aware strobe after settling, and a proper reset handles power-up unknowns. The adapted testbench runs correctly on the timed netlist and, crucially, surfaces a real power-up unknown that the broken testbench had masked. That unknown is exactly what the next chapter will root-cause, so this hands off a trustworthy FSM environment to X-propagation.

Foundation14 min readGLSFSMTestbenchbindWorked Example

Chapter 5 · Section 5.5 · GLS Testbench Adaptation

Project thread — the design advances from the counter (Ch2–4) to an FSM here. The adapted, trustworthy FSM testbench carries into Chapter 6 (root-causing an X in the FSM) and toward the UART/timer and mini-SoC later.

1. Why Should I Learn This?

This is where 5.1–5.4 become one working procedure on a real design.

  • You take a broken RTL testbench to a trustworthy gate-level one.
  • You see all four adaptations (refs, stimulus, checker, reset) applied together.
  • You watch the adapted testbench surface a real X the broken one hid.

It closes the chapter and sets up X-propagation (Chapter 6).

2. Real Silicon Story — the FSM testbench that hid a real X

An FSM's RTL testbench, run on the netlist, was quietly "adjusted" to stop failing — an internal state check was commented out, and outputs were sampled loosely. It then passed.

But the FSM had a state flop that never saw a clean reset, so it powered up X. The loosened testbench masked it (the X reference was gone; the X output slipped through a loose compare). A properly adapted testbench — port/bind checking, strobed X-aware sampling, proper reset — re-exposed the X as a real power-up bug.

Lesson: adapting a testbench is not about making failures go away — it is about making the testbench trustworthy so real failures (like a power-up X) show correctly.

3. Concept — adapting the FSM testbench, step by step

The FSM (project design): a small controller — IDLE → RUN → DONE — with a datapath counter, driven by start/mode, producing busy/done/count.

The four adaptations (integrating the chapter):

  • (5.2) References: drop dut.state; check ports (busy, done, count) and bind a checker to the DUT; where state visibility is needed, preserve the state signal.
  • (5.3) Stimulus: drive start/mode a skew after the active edge, clear of the timing window; clean clock.
  • (5.4) Checker: sample outputs on an X-aware strobe after settling ($isunknown, !==).
  • (5.1/reset): apply a proper reset sequence before comparing — netlist flops power up X (2.6).

What the adapted run shows: X at power-up until reset, then legal FSM transitions with real clk-to-Q timing — and any genuine X (e.g. an unreset state flop) surfaces honestly.

Scope: representative; the adapted TB reveals real behaviour — it doesn't fix timing, and GLS stays dynamic (STA signs off, 0.3).

Adapted FSM testbench: port/bind checking on preserved state, timing-aware stimulus, X-aware strobe checker, proper resetresetdriveobserveflagsXProper resethandles power-up X (2.6)Timing-aware stimulusstart/mode a skew AFTERedge (5.3)FSM netlist (timed)IDLE→RUN→DONE + counterdatapathbind checkerports + PRESERVED state(5.2)X-aware strobecheckersample after settle;$isunknown/!== (5.4)Real X surfaceshonestlyunreset state flop → Ch612
Figure 1 — the adapted FSM testbench (representative). The broken RTL testbench referenced dut.state (renamed), drove at the edge, sampled at the edge with loose X handling, and skipped a proper reset. The adapted testbench checks PORTS + a BIND checker against a PRESERVED state signal (5.2), drives start/mode a skew AFTER the edge (5.3), samples outputs on an X-aware STROBE (5.4), and applies a proper RESET for power-up X. It runs correctly on the timed netlist and surfaces real X honestly (Ch6).

4. Mental Model — re-fit the RTL testbench to gate dialect

The FSM testbench is a good tool built for the RTL house; the netlist house has the same address (ports) but rearranged rooms.

  • Re-address internal probes to ports/bind (5.2).
  • Re-time the drive to hand off between strides (5.3).
  • Re-time the read to after the play (5.4).
  • Re-establish a known start (reset) so the FSM isn't born blind (X).

Same intent, translated — and now honest about the X the old fit hid.

5. Working Example — FSM RTL (tri-HDL) and the adapted testbench

The FSM RTL, in three languages:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// SystemVerilog — small FSM: IDLE -> RUN -> DONE, sync active-low reset
module fsm (input logic clk, rst_n, start, output logic busy, done);
  typedef enum logic [1:0] {IDLE, RUN, DONE} state_t;
  state_t state;
  always_ff @(posedge clk)
    if (!rst_n) state <= IDLE;
    else case (state)
      IDLE: state <= start ? RUN : IDLE;
      RUN:  state <= DONE;
      DONE: state <= IDLE;
    endcase
  assign busy = (state == RUN);
  assign done = (state == DONE);
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Verilog-2001 — same FSM
module fsm (clk, rst_n, start, busy, done);
  input clk, rst_n, start; output busy, done;
  reg [1:0] state;
  localparam IDLE=2'd0, RUN=2'd1, DONE=2'd2;
  always @(posedge clk)
    if (!rst_n) state <= IDLE;
    else case (state)
      IDLE: state <= start ? RUN : IDLE;
      RUN:  state <= DONE;
      DONE: state <= IDLE;
      default: state <= IDLE;
    endcase
  assign busy = (state == RUN);
  assign done = (state == DONE);
endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- VHDL — same FSM
library ieee; use ieee.std_logic_1164.all;
entity fsm is port (clk, rst_n, start : in std_logic; busy, done : out std_logic); end entity;
architecture rtl of fsm is
  type state_t is (IDLE, RUN, DONE); signal state : state_t;
begin
  process (clk) begin
    if rising_edge(clk) then
      if rst_n = '0' then state <= IDLE;
      else case state is
        when IDLE => if start = '1' then state <= RUN; else state <= IDLE; end if;
        when RUN  => state <= DONE;
        when DONE => state <= IDLE;
      end case; end if;
    end if;
  end process;
  busy <= '1' when state = RUN  else '0';
  done <= '1' when state = DONE else '0';
end architecture;

The adapted testbench (integrating 5.2–5.4 + reset):

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Adapted FSM testbench for GLS — REPRESENTATIVE
module tb_fsm;
  logic clk = 0, rst_n, start; logic busy, done;
  fsm u_dut (.clk(clk), .rst_n(rst_n), .start(start), .busy(busy), .done(done));
  always #5 clk = ~clk;                                   // (5.3) clean clock
 
  // (5.2) bind a checker to the instance; check PORTS (+ a PRESERVED state if needed)
  bind fsm fsm_checker u_chk (.clk(clk), .rst_n(rst_n), .busy(busy), .done(done));
 
  default clocking cb @(posedge clk); input #3 busy, done; endclocking  // (5.4) strobe after settle
 
  initial begin
    rst_n = 0; start = 0;               // (reset) hold reset -> clears power-up X (2.6)
    repeat (2) @(posedge clk); #2 rst_n = 1;             // (5.3) release a skew after the edge
    #2 start = 1; @(posedge clk); #2 start = 0;          // (5.3) drive clear of the window
    @(cb);                                               // (5.4) sample on strobe
    if ($isunknown(cb.busy) || $isunknown(cb.done))      // (5.4) X = failure to investigate
      $error("X on FSM outputs after reset");            // <- surfaces a REAL power-up X (Ch6)
  end
endmodule

Practical context (representative, tool-neutral):

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
gls/
  rtl/fsm.v            # FSM RTL (above)
  netlist/fsm.vg       # synthesized (state encoded into flops)
  lib/cells.v          # cell models
  sdf/fsm.sdf          # timing (Ch4)
  tb/tb_fsm.v          # ADAPTED testbench (bind, strobe, reset, timing-aware)
# Flow: compile -> $sdf_annotate @0 -> reset -> stimulus (after-edge) -> strobe check
# Result: X at power-up -> reset -> legal transitions; any REAL X surfaces honestly (Ch6).

The adapted FSM on the timed netlist, as a real waveform:

Adapted FSM on the timed netlist: X at power-up until reset, then legal transitions (with a real X surfacing)

9 cycles
The FSM outputs are X until reset, then follow IDLE, RUN, DONE with clk-to-Q timing; a state bit that missed reset stays Xpower-up Xpower-up XRUN → busyRUN → busyDONE → doneDONE → doneclkrst_nstartbusyXXdoneXXstate_bad (unreset)XXXXXXXXXt0t1t2t3t4t5t6t7t8
Representative. busy/done are X at power-up (state flops unknown, 2.6) until reset. After reset and start, the FSM steps IDLE->RUN (busy)->DONE (done), each a clk-to-Q delay after the edge. state_bad models a state flop that never saw a clean reset — it stays X, and the X-aware strobe checker flags it: a real bug for Chapter 6, no longer masked.

6. Debugging Session — an FSM testbench that "passed" by hiding an X

1

An FSM testbench passes on the netlist only because it was loosened — the internal state check was removed and outputs sampled loosely — masking a real power-up X from a state flop that never saw a clean reset; a properly adapted testbench re-exposes it

ADAPT FOR TRUST, NOT TO SILENCE FAILURES
Symptom

The FSM testbench "passes" on the netlist — but only after an internal state check was commented out and outputs were compared loosely. The FSM occasionally powers up in a bad state in a fuller sim.

Root Cause

The testbench was loosened to silence failures, not adapted for correctness. Removing the dut.state check hid a signal that was reading X; a loose == let an X output slip through as a non-failure. Underneath, a state flop never saw a clean reset (a reset-gap / unconnected reset, 2.6), so it powered up X — a real bug. The loose testbench masked it: no reference, no X check, no strobe. The distinction is the whole point of the chapter — loosening ≠ adapting. A masked failure is still a failure.

Fix

Adapt the testbench properly: bind a checker and check ports (and a preserved state) instead of deleting the check (5.2); drive stimulus clear of the window (5.3); sample on an X-aware strobe ($isunknown, !==, 5.4); and apply a proper reset (2.6). Re-run: the adapted testbench re-exposes the power-up X as a real failure — precisely what a trustworthy GLS environment should do. The lesson: adapting a testbench makes it trustworthy so real behaviour (like a power-up X) shows correctly — it is not loosening checks to make failures disappear. Root-causing that X is Chapter 6. (The adapted run is still dynamic — STA signs off timing, 0.3.)

7. Common Mistakes

  • Loosening checks to "pass" on the netlist. Masks real bugs (like power-up X).
  • Deleting internal references instead of re-binding. Use bind + preserved signals (5.2).
  • Driving/sampling at the edge on the FSM (5.3/5.4).
  • Skipping a proper reset — the FSM powers up X (2.6).
  • Treating the adapted run as timing closure. STA signs off (0.3).

8. Industry Best Practices

  • Adapt, don't loosen — keep coverage while fixing RTL-only assumptions.
  • bind a reusable checker to the FSM (RTL + netlist), preserve only needed internals.
  • Timing-aware stimulus + X-aware strobe for the FSM outputs.
  • Reset first, compare second — always, on the netlist.
  • Let real X/timing surface — that is GLS doing its job (defer closure to STA).

Senior Engineer Thinking

  • Beginner: "I removed the failing state check, so the FSM testbench passes now."
  • Senior: "That masks a possible real X. I bind a checker, preserve state, strobe with $isunknown, and reset properly — if the X is real, I want it to fail. Adapting is for trust, not silence."

The senior adapts for trustworthiness and lets real failures surface, deferring timing to STA.

Silicon Impact

The FSM capstone shows the stakes of adaptation on a controller — the kind of block whose bad power-up state can hang a whole system. A loosened testbench that "passes" lets a real power-up X (unreset state flop) reach silicon, where the FSM comes up in an illegal state and hangs or misbehaves intermittently (the reset-gap escape, 2.6/0.3). A properly adapted testbench surfaces that X in simulation, where it is cheap to fix. The discipline of adapting-not-loosening — ports/bind, timing-aware stimulus, X-aware strobing, proper reset — is what makes the FSM's GLS run a real safety net.

Engineering Checklist

  • Replaced dut.state with port/bind checking (+ preserved state) (5.2).
  • Drove start/mode a skew after the edge, clean clock (5.3).
  • Sampled outputs on an X-aware strobe ($isunknown, !==) (5.4).
  • Applied a proper reset before comparing (power-up X, 2.6).
  • Adapted, not loosened — let real X/failures surface; deferred timing to STA.

Try Yourself

  1. Synthesize the FSM and run the original RTL testbench on the netlist — watch it break (dut.state X, edge-sample mismatches).
  2. Observe: the breakage is RTL-only assumptions (5.1).
  3. Change: apply all four adaptations — bind/ports, after-edge stimulus, X-aware strobe, proper reset.
  4. Expect: the FSM runs correctly (X at power-up → reset → IDLE/RUN/DONE). Then leave one state flop's reset disconnected and confirm the adapted checker flags the resulting X — a real bug for Chapter 6.

Any free Verilog simulator runs the FSM + adapted testbench end-to-end (bind, clocking blocks, $isunknown). No paid tool required.

Interview Perspective

  • Weak: "I made the FSM testbench pass on the netlist by removing the failing checks."
  • Good: "I adapted it — port/bind checking, timing-aware stimulus, strobed X-aware sampling, proper reset."
  • Senior: "I bind a checker and check ports plus a preserved state, drive stimulus clear of the window, strobe outputs with $isunknown/!==, and reset properly. The point is trust: the adapted testbench runs correctly and re-exposes a real power-up X the loosened one hid — which I root-cause next. Adapting reveals real behaviour; STA closes timing."

9. Interview / Review Questions

10. Key Takeaways

  • Adapting the FSM testbench integrates the chapter: port/bind checking (+ preserved state) (5.2), timing-aware stimulus (5.3), an X-aware strobe checker (5.4), and a proper reset for power-up X (2.6).
  • The FSM advances the project thread from the counter to a controller (IDLE → RUN → DONE + datapath), carried forward to Chapter 6 and beyond.
  • The adapted testbench runs correctly on the timed netlistX at power-up → reset → legal transitions with real clk-to-Q timing.
  • Adapting ≠ loosening: the goal is a trustworthy testbench that lets real behaviour show — it re-exposes a real power-up X (unreset state flop) that a loosened testbench masked.
  • That surfaced X is the hand-off to Chapter 6 (X-propagation); the adapted run is timed but dynamic — STA signs off (0.3). This closes Chapter 5.

Quick Revision

Adapt the FSM testbench: port/bind checking (+ preserved state) (5.2), stimulus a skew after the edge (5.3), X-aware strobe ($isunknown/!==) (5.4), proper reset (2.6). Runs correctly on the timed netlist (X → reset → IDLE/RUN/DONE). Adapt ≠ loosen — it re-exposes a real power-up X (→ Ch6), doesn't mask it. Dynamic; STA signs off. Chapter 5 complete; next: Chapter 6 — X-propagation.