A clocking block declares once when the testbench samples and when it drives, relative to its clocking event. The defaults are chosen to dodge the race window on both sides.
Input skew #1step samples the value held just before the edge, in the Preponed region. Output skew #0 drives in the Re-NBA region of the same step, after the RTL has read its inputs.
Skew | Takes effect | Meaning |
|---|---|---|
| Preponed region | Samples the stable pre-edge value, the same value the RTL flops capture |
| Re-NBA region, same time step | Lands after RTL reads for this edge; the DUT consumes it at the next edge |
| Re-NBA region of the time step 2ns after the clocking event | Same region, later time slot: the pin changes 2ns after the edge, which reads cleanly on waves |
| One precision unit, not a tunable delay | Never confuse it with a 1ns delay |
The listing below is an interface with no testbench around it, so it is not run in this course's checks. Icarus 13 has no clocking blocks; Verilator 5.048 compiles them.
// not-run: interface only, no testbench module; Icarus 13 has no clocking blocks (Verilator 5.048 compiles it)
interface bus_if (input logic clk);
logic valid, ready;
logic [7:0] data;
clocking cb @(posedge clk);
default input #1step output #0;
input ready; // TB samples: value just BEFORE the edge
output valid, data; // TB drives: after the edge, in Re-NBA
endclocking
modport tb (clocking cb); // TB sees ONLY the skewed view
endinterfaceSAME SCENARIO, NOW THROUGH cb - time step at posedge clk
PREPONED
+------------------------------------------------------+
| cb inputs sampled = values from BEFORE the edge |
| (no process has run yet - ordering is impossible) |
+------------------------------------------------------+
|
ACTIVE / NBA v
+------------------------------------------------------+
| RTL always_ff runs: reads din (old value), schedules |
| dout <= old value in NBA. The TB has touched NOTHING |
| in this region. No race possible. |
+------------------------------------------------------+
|
RE-NBA (TB drive lands) v
+------------------------------------------------------+
| cb drive takes effect HERE - after all RTL reads for |
| this edge are done. RTL consumes it at the NEXT |
| posedge. Deterministic in every simulator. |
+------------------------------------------------------+
ORDER A and ORDER B from the race lesson now produce
IDENTICAL results - ordering no longer matters.It moves the TB's sample point and drive point into regions where no RTL process is competing. The block does not pick a friendlier order; it makes every legal order produce the same result.
The bottom row, dout, still holds the pre-edge value in each slot where din just changed. The drive lands after the edge and the sample keeps the value from before it.
Slot 0: din is 12, so dout still holds 00 (the pre-edge value).
Slot 1: din is 34, so dout takes 12.
Slot 2: din holds 34, so dout takes 34.
Slot 3: din is 56, so dout still holds 34.
Slot 4: din is 78, so dout takes 56.
Slot 5: din holds 78, so dout takes 78.
Every drive goes through the clocking block with a nonblocking assignment, and every sample reads the clocking input. One clock authority per boundary: synchronize with the clocking event, never with a bare edge in some tasks and the block in others.
Drive as vif.cb.sig <= val and read as vif.cb.sig. Touching the raw pin reopens the race.
##N needs a default clocking in the enclosing module, interface, program or checker. Class and package code cannot declare one, so it uses repeat (N) @(vif.cb);.
##[1:4] range syntax belongs to assertions and sequences only. Drivers and monitors use plain ##N.
#2ns is used instead of #0. Which region does the drive land in?Still the Re-NBA region, but of the time step 2ns after the clocking event: same region, later time slot.
##1 with no default clocking in its scope. What happens?The delay has no clocking to resolve against, so it is an error. A class cannot declare a clocking block, so write repeat (1) @(vif.cb); instead.
The runnable equivalent stages the same contract without same-edge gambling. A settled pre-edge drive is captured with one-cycle latency, while a drive landing just after the edge waits for the next one.
The clocking-block form itself stays in the interface listing above. Same-step drive-and-sample ordering is exactly what this topic refuses to rely on.
timeunit 1ns; timeprecision 1ps;
module flop8 (
input logic clk,
input logic [7:0] d,
output logic [7:0] q
);
always_ff @(posedge clk) q <= d;
endmodule