In RTL simulation every assign and always_comb evaluates in zero simulation time. A chain of ten gates settles at the same timestamp as its input change.
The simulator still has to order the evaluations. It uses delta cycles: tiny, zero-width steps inside one timestamp, in which each dependent assignment is re-evaluated until nothing changes.
This is why RTL simulation cannot tell you whether a design is fast enough. Timing comes from the synthesized netlist and static timing analysis. Simulation only checks that the function is right.
Gate-level simulation uses real delays. The default model for a Verilog assign #3 y = a; is inertial: if the input pulse is shorter than the delay, the output never changes.
This approximates real gates, which tend to attenuate very short pulses. The sharp cutoff is an idealized digital simulation rule, not a universal physical guarantee. Measured response depends on the cell, load and pulse shape.
A transport delay passes every pulse after the delay, even a very short one. An inertial delay swallows pulses shorter than the delay.
Zero-delay RTL simulation | Gate-level simulation / real silicon | |
|---|---|---|
Gate delay | None | From the cell library |
Glitches | Mostly invisible (they live inside delta cycles) | Real, and they can propagate |
Pulses shorter than a gate delay | Not modelled | Attenuated in silicon, filtered by the idealized inertial rule |
Used for | Functional verification | Timing-annotated functional checks, glitch and power analysis (timing sign-off itself is STA) |
Watch out. It ignores the short pulse and follows the long one 3 units later.
Slots 1 to 3: out is x. The #3 assign has not driven it yet.
Slots 4 to 7: out reads 0. The 2-unit pulse on in (slots 5 to 6) is shorter than the delay, so it is swallowed.
Slots 8 to 10: in is 1 but out is still 0. The 5-unit pulse needs 3 units to cross.
Slot 11: out rises, 3 units after in rose.
Slots 11 to 15: out follows in at 1.
Slot 16: out falls, 3 units after in fell.
Glitches settle before the capture edge when timing is met; follow these rules.
Trust a same-domain registered output when it is stable through the receiving setup and hold window. It samples once per cycle. It then sees only the settled value, so settled glitches are safe.
Never drive async controls from raw combinational logic. Async means the control acts at once, not on a clock edge. Clock gates, resets, output enables, decoder strobes, and anything feeding an async input all count. Registering into the destination domain is only the start. Clocks need glitch-free switching logic, resets need a dedicated synchroniser with ordered release, and output enables need settled timing of their own.
Or reduce transitions instead of assuming they are glitch-free. Use a Gray-coded counter, where only one bit changes per step. Or assert one-hot requests one at a time from idle. A single-bit change alone does not remove reconvergent hazards. The 2:1 mux below glitches when only its select changes.
Know the classic glitch. A static-1 hazard is a short drop on a signal that should stay 1. A 2:1 mux built from AND-OR gates shows it. With both data inputs at 1, the output can briefly drop to 0 when the select changes. The select and its inversion briefly overlap at 0 during the change.
Watch y. It should stay 1, but it drops to 0 for one slot.
Slots 1 to 2: the gates are still settling from x.
Slots 3 to 5: y holds 1 with sel at 1 (t0 is 0, t1 is 1).
Slot 6: sel drops to 0. Nothing else has moved yet.
Slot 7: sel_n is 1 and t1 has fallen to 0. y still reads 1.
Slot 8: y dips to 0 for one slot, although t0 is already 1.
Slots 9 to 11: t0 holds 1 and y is back to 1.
Nothing. The pulse is shorter than the delay, so it is absorbed. A 5 ns pulse would appear at the output, delayed by 3 ns.
At t = 5, the same timestamp. Delta cycles order the evaluations inside zero simulation time.
The settled value, as long as it is stable through setup and hold. A same-domain registered output is safe from settled glitches. Async controls and crossings into another clock, reset or enable domain need their own protocols.
These testbenches check instant settling and pulse filtering.
module tb_delta;
timeunit 1ns; timeprecision 1ps;
logic x = 0;
wire c1, c2, c3;
assign c1 = ~x;
assign c2 = ~c1;
assign c3 = ~c2; // three inverters in series
int errors = 0;
initial begin
#5;
x = 1;
#0; // let delta cycles run
if (c3 !== 1'b0) begin // ~~~1 = 0 : settled at the SAME time (t = 5)
$display("ERROR: c3=%b at t=%0t, expected 0", c3, $time);
errors++;
end
if (errors != 0) $fatal(1, "FAIL: tb_delta errors=%0d", errors);
$display("PASS: a chain of assigns settles in zero simulation time (t=%0t)", $time);
$finish;
end
endmodulemodule tb_inertial;
timeunit 1ns; timeprecision 1ps;
reg a = 0;
wire y;
assign #3 y = a; // 3-unit inertial delay
integer rises = 0;
always @(posedge y) rises = rises + 1;
initial begin
#10 a = 1; #2 a = 0; // 2-unit pulse < 3 : swallowed
#10 a = 1; #5 a = 0; // 5-unit pulse > 3 : passes through
#10;
if (rises == 1) $display("PASS: the short pulse was filtered, the long one passed");
else $fatal(1, "FAIL: rises=%0d", rises);
$finish;
end
endmodule