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
Xthe 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) andbinda checker to the DUT; where state visibility is needed, preserve thestatesignal. - (5.3) Stimulus: drive
start/modea 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).
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:
// 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// 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-- 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):
// 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
endmodulePractical context (representative, tool-neutral):
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 cycles6. Debugging Session — an FSM testbench that "passed" by hiding an X
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 FAILURESThe 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.
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.
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.
binda 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. Ibinda checker, preservestate, strobe with$isunknown, and reset properly — if theXis 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.statewith port/bindchecking (+ preservedstate) (5.2). - Drove
start/modea 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
- Synthesize the FSM and run the original RTL testbench on the netlist — watch it break (
dut.stateX, edge-sample mismatches). - Observe: the breakage is RTL-only assumptions (5.1).
- Change: apply all four adaptations —
bind/ports, after-edge stimulus,X-aware strobe, proper reset. - Expect: the FSM runs correctly (
Xat power-up → reset →IDLE/RUN/DONE). Then leave one state flop's reset disconnected and confirm the adapted checker flags the resultingX— 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
binda checker and check ports plus a preservedstate, 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-upXthe 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/
bindchecking (+ preservedstate) (5.2), timing-aware stimulus (5.3), anX-aware strobe checker (5.4), and a proper reset for power-upX(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 netlist —
Xat 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
Xis 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/
bindchecking (+ preservedstate) (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-upX(→ Ch6), doesn't mask it. Dynamic; STA signs off. Chapter 5 complete; next: Chapter 6 — X-propagation.