Zero-delay functional RTL simulation cannot establish physical timing. This teaching model adds fixed clock-to-Q, logic and capture-clock delays and checks transitions around capture edges. It has no library characterization, uncertainty, variation or analog metastability model.
Clock-to-Q is fixed at 0.35 ns for both min and max, with 0.12 ns setup and 0.06 ns hold. Real STA uses distinct early/late values. A violated window drives the modeled output to X as a diagnostic, not a prediction of silicon behavior. Monitoring starts at 5 ns after startup settles.
Path | Logic (ns) | Skew (ns) | Setup slack | Hold slack | Expected after warmup (not newly simulated) |
|---|---|---|---|---|---|
0 | 1.45 | 0.00 | +0.08 | +1.74 | clean |
1 | 1.64 | 0.00 | −0.11 | +1.93 | 28 setup violations |
2 | 1.65 | +0.30 | +0.18 | +1.64 | clean: slightly longer logic, helped by useful skew |
3 | 0.05 | 0.00 | +1.48 | +0.34 | clean: short path, no skew |
4 | 0.05 | +0.38 | +1.86 | −0.04 | 28 hold violations |
5 | 0.25 | +0.38 | +1.66 | +0.16 | clean: 0.2 ns of added delay fixes hold |
Read each pair of bars as one path: the upper bar is setup slack, the lower bar is hold slack, and anything left of the zero line fails.
Paths 0 to 2 have long logic, so setup is small and hold is large. Path 1 is the only setup failure (-0.11 ns).
Paths 3 to 5 have short logic, so the picture flips: setup is large and hold is small. Path 4 is the only hold failure (-0.04 ns).
Skew moves both bars at once: path 2's +0.30 ns skew lifts setup to +0.18 ns and lowers hold to +1.64 ns; path 4's +0.38 ns skew is what pushes hold below zero.
Path 5 is path 4 with 0.20 ns more logic: hold rises by 0.20 to +0.16 ns and setup falls by 0.20 to +1.66 ns.
Path 1 setup: T + skew - tsu - tcq - logic = 2.00 + 0.00 - 0.12 - 0.35 - 1.64 = -0.11
Path 2 setup: 2.00 + 0.30 - 0.12 - 0.35 - 1.65 = +0.18
Path 4 hold: tcq + logic - skew - thold = 0.35 + 0.05 - 0.38 - 0.06 = -0.04
Path 5 hold: 0.35 + 0.25 - 0.38 - 0.06 = +0.16The checker below compares arithmetic against independent expected slack constants, verifies violation signs in both directions, and checks exact counts across 28 monitored captures (5 through 59 ns, plus each path's skew). It finds 28 setup violations on path 1 and 28 hold violations on path 4. Per-event and per-path failures print timestamps, measured margins and expected counts, then exit with a fatal verdict.
Path | Data transition | Relevant capture / deadline | Expected observation |
|---|---|---|---|
1 | 5 + 0.35 + 1.64 = 6.99 ns | Next capture 7.00; setup deadline 6.88 ns | At 7.00, data age 0.01 < 0.12 ns: setup fails by 0.11 ns |
2 | 5 + 0.35 + 1.65 = 7.00 ns | Next capture 7.30; setup deadline 7.18 ns | Data age 0.30 ns: setup passes; no coincident capture |
4 | 5 + 0.35 + 0.05 = 5.40 ns | Same capture 5.38; hold deadline 5.44 ns | At 5.40, data age from clock 0.02 < 0.06 ns: hold fails by 0.04 ns |
Capture arrives 0.30 ns later. Without skew, path 2 would have -0.12 ns setup slack; with skew it has +0.18 ns. Its hold slack drops from +1.94 to +1.64 ns, and outgoing full-cycle setup paths lose 0.30 ns with fixed other terms.
The delay-annotated model and the six-path testbench that checks violations against slack.
`timescale 1ns/1ps
// Teaching-only local windows. #0 settles active events without advancing time.
module dff_chk #(parameter real TCQ = 0.35, TSU = 0.12, THD = 0.06) (
input logic clk, d,
output logic q,
output int setup_viol, hold_viol
);
realtime t_clk = -100.0, t_d = -100.0;
localparam real ARM_TIME = 5.0;
initial begin setup_viol = 0; hold_viol = 0; q = 1'b0; end
always @(d) begin
t_d = $realtime;
#0;
if ($realtime >= ARM_TIME) begin
if ($isunknown(d)) $fatal(1, "%m unknown D at %0.3f ns", $realtime);
if (t_clk == t_d) $fatal(1, "%m unsupported coincident D/clock at %0.3f ns", t_d);
if ($realtime - t_clk < THD && t_clk >= ARM_TIME) begin
hold_viol++; q <= #TCQ 1'bx;
$display("%m HOLD at %0.3f ns: D=%b clock=%0.3f age=%0.3f required=%0.3f", $realtime, d, t_clk, $realtime-t_clk, THD);
end
end
end
always @(posedge clk) begin
t_clk = $realtime;
#0;
if ($realtime >= ARM_TIME && $isunknown(d))
$fatal(1, "%m unknown D at capture %0.3f ns", $realtime);
if ($realtime >= ARM_TIME && t_clk == t_d)
$fatal(1, "%m unsupported coincident D/clock at %0.3f ns", t_clk);
if ($realtime >= ARM_TIME && $realtime - t_d < TSU) begin
setup_viol++; q <= #TCQ 1'bx;
$display("%m SETUP at %0.3f ns: D=%b last_D=%0.3f age=%0.3f required=%0.3f", $realtime, d, t_d, $realtime-t_d, TSU);
end else
q <= #TCQ d;
end
endmodule
// launch flop -> combinational delay -> capture flop whose clock arrives SKEW later
module timing_path #(parameter real T = 2.0, LOGIC = 1.45, SKEW = 0.0) (
input logic clk,
output int setup_viol, hold_viol,
output real slack_setup, slack_hold
);
localparam real TCQ = 0.35, TSU = 0.12, THD = 0.06;
initial begin
if (T != 2.0 || LOGIC < 0.0 || LOGIC >= T || SKEW < 0.0 ||
SKEW >= TCQ + LOGIC || TCQ + LOGIC > T + SKEW)
$fatal(1, "%m unsupported timing model: T=%0.3f LOGIC=%0.3f SKEW=%0.3f", T, LOGIC, SKEW);
end
logic q1, d1, d2, q2, clk2;
int sv1, hv1, sv2, hv2;
assign d1 = ~q1; // launch a new value every cycle
dff_chk #(TCQ, TSU, THD) ff1 (.clk(clk), .d(d1), .q(q1), .setup_viol(sv1), .hold_viol(hv1));
assign #(LOGIC) d2 = q1; // combinational path delay
assign #(SKEW) clk2 = clk; // only nonnegative skew is supported
dff_chk #(TCQ, TSU, THD) ff2 (.clk(clk2), .d(d2), .q(q2), .setup_viol(sv2), .hold_viol(hv2));
assign setup_viol = sv2;
assign hold_viol = hv2;
assign slack_setup = T + SKEW - TSU - TCQ - LOGIC; // data must arrive before the next capture edge minus setup
assign slack_hold = TCQ + LOGIC - SKEW - THD; // data must not arrive before the previous edge + hold
endmodule
The checker compares arithmetic against independent expected slack constants, verifies violation signs in both directions, and counts 28 monitored captures (5 to 59 ns, plus each path's skew). Run with Icarus Verilog 13.0 (2026-10-16): prints PASS, with 28 setup violations on path 1 and 28 hold violations on path 4.
The three mutations suggested in the lesson (path 1 logic 1.50, path 4 skew 0.48, path 1 logic 1.65) are proposed checks and were not run.
The model has no library characterisation, uncertainty, variation or analogue metastability model. Real STA uses distinct early and late values.