Skip to content

SystemVerilog · Module 15

Clocking Blocks — Deep Dive, SVA, Coverage, Multi-Clock SoC, CDC

SystemVerilog clocking blocks at production depth — input/output skews, the ##N operator, multi-clocking-block patterns, default clocking, and the deeper architectural role as the foundation of SVA, functional coverage, multi-clock SoC verification, and clock-domain crossing (CDC) discipline.

Module 15 · Page 15.2 — Deep Dive

A clocking block packages a clock edge, its signal directions, and the sampling skews into a single named object inside an interface. The basics (covered at /learnings/systemverilog/clocking-blocks) explain how it eliminates the Active-region TB-DUT race. This deep-dive lesson goes further — into the architectural roles the clocking block plays as the foundation of SVA, functional coverage, multi-clock-domain verification, and clock-domain crossing (CDC) discipline. Every modern UVM testbench that touches a synchronous interface relies on it; every multi-clock SoC has one per clock domain; every SVA property is anchored to one; every cycle-sampled covergroup samples on one. Understanding the clocking block at this depth is the prerequisite to debugging the verification stack of any non-trivial chip.

1. Engineering Problem — The Three Things a Clocking Block Solves

A modern SoC verification environment faces three layered problems that pre-clocking-block testbenches couldn't solve cleanly:

  • The TB-DUT race at the clock edge — module-based testbench code wakes in the Active region, before the DUT's NBA-region flip-flop updates commit. The TB reads pre-edge values; the DUT thinks it has updated them. Covered at the introductory level in /learnings/systemverilog/clocking-blocks and in 15.1 program-block.
  • Multi-clock-domain coordination. A real SoC has 5–50 clock domains: a fast core clock, an interconnect clock at half speed, multiple DDR clocks, separate USB/PCIe/Ethernet clocks, an always-on (AON) clock. Every domain has its own sampling discipline, its own setup/hold requirements, and its own verification VIP. Without a per-domain clocking primitive, every monitor and driver re-invents the timing wheel — inconsistently.
  • The SVA / coverage / CDC dependency chain. SystemVerilog assertions sample on a clock; functional coverage often samples on a clock; CDC verification depends on knowing exactly when signals are sampled on each side of a domain boundary. The clocking block is the single construct that anchors all three. Mis-specify the clocking block and your assertions evaluate on wrong edges, your coverage misses real bin hits, your CDC analysis flags false positives.

The clocking block exists to be the canonical timing contract for everything in the verification environment that has to coordinate with a clock. This lesson treats it at that depth.

2. Mental Model — The Contract Attached to a Clock Edge

The picture every engineer carries:

A clocking block is a typed contract attached to a clock edge. It says: "for this clock, here are the signals I observe, here are the signals I drive, here is exactly how long before the edge I sample inputs, and here is exactly how long after the edge I drive outputs." Every consumer of that contract — testbench code, SVA property, covergroup — automatically gets race-free, skew-correct access to those signals without having to think about scheduler regions again.

Four invariants this picture preserves:

  • Direction is from the testbench's perspective. input in a clocking block means "the TB reads this signal" (the DUT drives it). output means "the TB drives this signal" (the DUT reads it). This is the opposite convention from a module's port list and is the most common source of clocking-block confusion.
  • Inputs are sampled at input_skew before the edge. The default is #1step — the last time step before the edge — guaranteeing the sample point is after any pending DUT updates from the previous edge have settled. Inputs read from the clocking block always hold post-settled DUT values.
  • Outputs are driven at output_skew after the edge. The default is #1 — one time unit after the edge — guaranteeing the DUT sees the new stimulus with full setup time before the next edge. Outputs written through the clocking block always reach the DUT with proper setup.
  • Multiple clocking blocks can coexist in one interface. Master and slave perspectives, fast and slow domains, transmit and receive clocks — each gets its own clocking block. The interface bundles the signals; the clocking blocks bundle the timing.

3. Visual Explanation — The Input/Output Skew Window

The clocking block's timing model in one figure: input signals are sampled at the input-skew window (just before each clock edge); output signals are driven at the output-skew window (just after each edge). The pre-edge sample and post-edge drive together eliminate both races — DUT-reads-stimulus-too-early and TB-reads-response-too-late.

Figure — clocking block timing: input sampled before edge, output driven after edge

12 cycles
Figure — clocking block timing: input sampled before edge, output driven after edgeINPUT sampled #1step before edge — v0 capturedINPUT sampled #1step b…OUTPUT driven #1 after edge — w1 appliedOUTPUT driven #1 after…DUT sees w1 fully settled at its next edgeDUT sees w1 fully sett…clkcb input (DUT→TB)v0v0v0v1v1v1v1v2v2v2v2v2cb output (TB→DUT)w0w0w1w1w1w1w2w2w2w2w3w3t0t1t2t3t4t5t6t7t8t9t10t11
At each clk posedge: clocking-block INPUT is sampled #1step before the edge (the SAMPLE marker), capturing the fully-settled DUT output from the previous cycle. Clocking-block OUTPUT is driven #1 after the edge (the DRIVE marker), giving the DUT a full cycle of setup time before its next sampling edge. Both races are structurally eliminated; the TB and DUT are temporally isolated from each other across every clock cycle.

The figure captures the asymmetry: inputs land before the edge (so the TB reads stable values), outputs land after the edge (so the DUT sees stable values for the next edge). This is the entire teaching of the clocking block timing model, compressed into one diagram.

4. Syntax & Semantics — Complete Reference

4.1 The general form

SystemVerilog — clocking block syntax inside an interface
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── General form ──────────────────────────────────────────────
clocking cb_name @(clocking_event);
    default input  #input_skew
            output #output_skew;        // default skews for all signals
 
    input  signal_a;                    // TB reads   — DUT output
    input  signal_b;
    output signal_c;                    // TB drives  — DUT input
    output signal_d;
    inout  signal_e;                    // bidirectional
    input  #2 signal_f;                 // per-signal skew override
    output #3 signal_g;
endclocking
 
// ── Real APB example inside an interface ──────────────────────
interface apb_if (input logic pclk);
    logic        presetn;
    logic        psel, penable, pwrite, pready, pslverr;
    logic [31:0] paddr, pwdata, prdata;
 
    // Testbench clocking block — captures the timing contract once
    clocking cb @(posedge pclk);
        default input  #1step           // sample just before posedge
                output #1;              // drive 1 time unit after posedge
 
        // Direction is from the TB's perspective:
        input  pready, pslverr, prdata; // TB reads (DUT drives these)
        output psel, penable, pwrite, paddr, pwdata;  // TB drives (DUT reads)
    endclocking
 
    // presetn is not in the clocking block — async signal, no clock skew needed
 
    modport TB  (clocking cb, output presetn);
    modport DUT (input  psel, penable, pwrite, paddr, pwdata, presetn,
                 output pready, pslverr, prdata);
endinterface

4.2 Direction conventions — TB perspective vs DUT perspective

Direction in clocking cbTB's roleDUT's roleSkew applied
input signal_areads the signaldrives the signalsampled at #input_skew before the edge
output signal_bdrives the signalreads the signalscheduled at #output_skew after the edge
inout signal_cboth reads + drivesboth drives + readsboth skews apply

The opposite-convention trap: a DUT module declares output [7:0] data_out (the DUT drives data_out). The same signal appears in the testbench's clocking block as input data_out (the TB reads data_out). Same wire, opposite direction keyword, depending on whose port you're looking at.

4.3 The full method set — @(cb), ##n, iff

SystemVerilog — wait, advance, and conditional-wait via clocking block
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── @(cb): wait for exactly one clocking event ────────────────
@(bus.cb);                  // wait for the next posedge pclk (as defined in cb)
 
// ── ##n: wait for exactly N clock cycles ──────────────────────
##1;                        // 1 clock cycle (syntactic sugar for @(default_cb))
##4;                        // 4 clock cycles — much cleaner than repeat(4) @(cb)
 
// ── ##[m:n]: range delay — wait between m and n cycles ────────
##[1:8];                    // wait between 1 and 8 cycles (non-deterministic)
##[0:4] signal;             // wait 0 to 4 cycles until signal is asserted
 
// ── @(cb iff condition): wait for clock edge WITH a condition ─
@(bus.cb iff bus.cb.pready);            // posedge where pready=1
@(bus.cb iff !bus.cb.pslverr);          // posedge where no error
@(bus.cb iff bus.cb.prdata == 32'hCAFE_0001);  // value-specific wait

4.4 Driving outputs — non-blocking is mandatory

SystemVerilog — non-blocking <= required for clocking block outputs
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── CORRECT: use <= (non-blocking) for clocking block outputs ─
bus.cb.psel   <= 1;         // scheduled to drive #1 after posedge pclk
bus.cb.paddr  <= 32'h1000;  // (default output skew applies)
bus.cb.pwrite <= 1;
 
// ── WRONG: blocking = inside a clocking block output ──────────
bus.cb.psel = 1;            // COMPILE ERROR or undefined — outputs require <=

The non-blocking requirement falls out of the skew implementation: the clocking block schedules the assignment in the NBA queue with the output skew added. A blocking = would bypass that scheduling, defeating the race-elimination purpose of the clocking block. Most simulators reject blocking assignment to clocking block outputs at compile time; some produce undefined behaviour.

4.5 default clocking — module-wide default

SystemVerilog — default clocking lets ##n omit the cb. prefix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
program clean_test (apb_if.TB bus);
    default clocking bus.cb;           // declare the program's default clocking
 
    initial begin
        ##4;                           // 4 cycles — refers to bus.cb automatically
        bus.cb.psel   <= 1;
        bus.cb.paddr  <= 32'h8000;
        bus.cb.pwrite <= 1;
        ##1;
        bus.cb.penable <= 1;
        @(bus.cb iff bus.cb.pready);   // wait for DUT ready
        bus.cb.psel    <= 0;
        bus.cb.penable <= 0;
        $exit;
    end
endprogram

Without default clocking, every ##n must be qualified: repeat (n) @(bus.cb);. The default-clocking declaration is one line; the readability win is significant in any program block longer than a few statements.

4.6 Multiple clocking blocks — master + slave perspectives

SystemVerilog — multiple clocking blocks for master and slave VIP perspectives
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface axi_if (input logic aclk);
    logic       aresetn, awvalid, awready, wvalid, wready, wlast, bvalid, bready;
    logic [31:0] awaddr, wdata;
    logic [1:0]  bresp;
 
    // ── Master clocking block: TB acts as AXI master ──────────
    clocking master_cb @(posedge aclk);
        default input #1step output #1;
        output awvalid, awaddr;              // master drives AW channel
        input  awready;                      // master reads slave's ready
        output wvalid, wdata, wlast;         // master drives W channel
        input  wready;
        input  bvalid, bresp;                // master reads B channel
        output bready;
    endclocking
 
    // ── Slave clocking block: TB acts as AXI slave ────────────
    clocking slave_cb @(posedge aclk);
        default input #1step output #1;
        input  awvalid, awaddr;              // slave reads AW channel
        output awready;                      // slave drives ready
        input  wvalid, wdata, wlast;
        output wready;
        output bvalid, bresp;                // slave drives B channel
        input  bready;
    endclocking
 
    modport MASTER (clocking master_cb, output aresetn);
    modport SLAVE  (clocking slave_cb);
endinterface

The same set of signals appears in both clocking blocks with opposite directions. The master-VIP program connects to axi_if.MASTER; the slave-VIP program connects to axi_if.SLAVE. Both get race-free access to their side of the bus.

5. Simulation View — Where Sampling and Driving Happen

The clocking block extends the IEEE 1800-2017 scheduling model with two skew operations:

  • Input skew (#1step default) schedules the sampling of the listed input signals to the Postponed region of the previous time step (one simulator-resolution-unit before the clock edge). The value the TB reads is the fully-settled post-NBA value from the previous edge.
  • Output skew (#1 default) schedules the driving of the listed output signals to one time unit after the clock edge. The DUT sees the new value with full setup time before its next sampling edge.

The asymmetry is deliberate. Inputs need to look backward (read what just settled); outputs need to look forward (drive what the DUT will sample next). The clocking block compresses both disciplines into one declaration so neither the TB nor the DUT can accidentally violate them.

#1step is a special time unit: not a fixed nanosecond, but one simulator time step before the specified clock edge — effectively, "sample in the Postponed region of the previous time slot." It is the recommended default input skew because it guarantees the sample point is before any active clock-edge evaluation.

6. Waveform — Skew in Action

The waveform below extends §3 with a realistic 3-cycle APB transaction. Pre-edge sampling captures DUT outputs; post-edge driving delivers TB stimulus. Notice how the DUT's pready is captured at the input-skew window of cycle 4 (the iff waits for the post-NBA-settled pready=1), and the TB's psel/penable deassertion lands at the output-skew window of cycle 6 (the DUT sees the clean deassertion on its cycle-7 sample).

Figure — clocking block over a 3-cycle APB transaction with iff-wait on pready

10 cycles
Figure — clocking block over a 3-cycle APB transaction with iff-wait on preadysetup phase begins — psel/paddr at output skewsetup phase begins — p…pready=1 captured at input skew — transaction completespready=1 captured at i…pclkpselpenablepaddrXXAAAAAXXXpreadyprdataXXXXXXDXXXt0t1t2t3t4t5t6t7t8t9
Cycle 0–1: setup phase — TB drives psel=1, paddr, pwrite via cb.output #1 skew. Cycle 2: access phase — penable=1 driven; pready sampled. Cycle 4: pready=1 captured at input-skew sample point; transaction completes. Cycle 5: TB drives psel=0, penable=0 (visible to DUT next edge). Every TB→DUT signal lands with full setup; every DUT→TB signal is sampled post-settle.

7. Synthesis — Not Applicable

clocking blocks are a procedural simulation construct. They have no hardware footprint and no place in synthesisable RTL — synthesis tools reject the clocking keyword outright. This section is intentionally omitted; the topic does not warrant it.

The corresponding hardware-side discipline is just the synchronous interface itself — always_ff @(posedge clk) blocks with registered outputs and adequate setup/hold margin from the STA tool. The clocking block is the verification-side counterpart that gives the TB the same timing discipline the hardware naturally has.

8. Verification View — The Architectural Role

This is the section that distinguishes the deep-dive from the basic lesson. Clocking blocks are not just a TB-DUT race fix — they are the timing foundation that SVA, functional coverage, multi-clock-domain VIPs, and CDC verification all build on. Each subsection walks one of the dependent layers.

8.1 APB transaction — the canonical pattern

SystemVerilog — production APB write/read tasks built on the clocking block
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
program apb_test (apb_if.TB bus);
    default clocking bus.cb;
 
    // ── APB Write ─────────────────────────────────────────────
    task automatic apb_write(input [31:0] addr, data);
        bus.cb.psel    <= 1;
        bus.cb.paddr   <= addr;
        bus.cb.pwrite  <= 1;
        bus.cb.pwdata  <= data;
        ##1;                                // setup phase
        bus.cb.penable <= 1;                // access phase
        @(bus.cb iff bus.cb.pready);        // wait for slave ready
        bus.cb.psel    <= 0;
        bus.cb.penable <= 0;
        ##1;
    endtask
 
    // ── APB Read ──────────────────────────────────────────────
    task automatic apb_read(input [31:0] addr, output [31:0] data);
        bus.cb.psel    <= 1;
        bus.cb.paddr   <= addr;
        bus.cb.pwrite  <= 0;
        ##1;
        bus.cb.penable <= 1;
        @(bus.cb iff bus.cb.pready);
        data = bus.cb.prdata;               // capture read data after pready
        bus.cb.psel    <= 0;
        bus.cb.penable <= 0;
        ##1;
    endtask
 
    initial begin
        logic [31:0] rd_data;
        bus.presetn = 0; ##4; bus.presetn = 1; ##2;
 
        apb_write(32'h0000_0010, 32'hABCD_1234);
        apb_read (32'h0000_0010, rd_data);
        assert (rd_data == 32'hABCD_1234)
            else $error("Read-back mismatch: got 0x%h", rd_data);
 
        $exit;
    end
endprogram

Every assignment uses <= through the clocking block; every wait uses @(bus.cb iff ...); every cycle delay uses ##n. No raw #delay values; no manual setup-time calculations. The clocking block makes the TB look like the spec: setup → access → ready → deassert.

8.2 SVA's dependence on clocking blocks

SystemVerilog Assertions sample on a clock. The clock can be specified per-property, per-property-set via default clocking, or inherited from the surrounding clocking block. Every concurrent assertion has a clocking-block dependency, explicit or implicit — and getting it wrong is the most common SVA bug class after the antecedent / consequent confusion itself.

SystemVerilog — SVA properties anchored to a clocking block
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface apb_if (input logic pclk);
    // ... signals ...
    clocking cb @(posedge pclk);
        default input #1step output #1;
        input pready, pslverr, prdata;
        output psel, penable, pwrite, paddr, pwdata;
    endclocking
 
    // ── Default clocking for ALL assertions in this scope ─────
    default clocking cb;
 
    // ── Properties — no explicit @(posedge pclk) needed ───────
    // The default clocking provides the implicit sampling event
    property psel_implies_penable_next_cycle;
        psel && !penable |=> penable;
    endproperty
    assert property (psel_implies_penable_next_cycle);
 
    // ── Property with explicit clocking — overrides the default
    property addr_stable_during_transfer;
        @(posedge pclk) disable iff (!presetn)
            (psel && penable && !pready) |=> $stable(paddr);
    endproperty
    assert property (addr_stable_during_transfer);
 
    // ── Property reading clocking-block-sampled signals ───────
    // Inside an assertion, signals reach the property via the SAME
    // input-skew sample that the TB uses. No race; values are settled.
    property no_error_on_completion;
        @(cb) (psel && penable && pready) |-> !pslverr;
    endproperty
    assert property (no_error_on_completion);
endinterface

Three ways assertions get their clock, in order of preference:

  1. default clocking — every property in the scope inherits this clock. The cleanest pattern; recommended for any interface with one dominant clock.
  2. @(cb) on the property itself — uses the clocking block by name, sharing its sample-skew semantics with the TB code.
  3. @(posedge clk) directly on the property — falls back to the raw clock edge, bypassing the clocking block's input skew. The property sees signals in the Active region, which can disagree with what the TB sees through the clocking block.

The third form is occasionally needed (e.g., for properties on signals that aren't in the clocking block), but every use deserves a comment explaining why. Mixed sampling — TB through clocking block, assertion through raw clock — produces the assertion thinks X happened but the TB log says Y divergence bugs.

8.3 Functional coverage's dependence on clocking blocks

covergroups sample on an event. The event is typically a clock edge, and binding the sampling event to a clocking block ensures the covergroup samples the same settled values the TB reads — same race-elimination, same skew discipline.

SystemVerilog — functional coverage covergroup anchored to a clocking block
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class apb_coverage;
    virtual apb_if vif;
 
    // ── Covergroup samples on the clocking block edge ─────────
    covergroup apb_cg @(vif.cb);
        cp_addr: coverpoint vif.cb.paddr {
            bins low_addrs  = {[0 : 32'h0FFF]};
            bins mid_addrs  = {[32'h1000 : 32'h7FFF]};
            bins high_addrs = {[32'h8000 : 32'hFFFF_FFFF]};
        }
        cp_write: coverpoint vif.cb.pwrite {
            bins write = {1};
            bins read  = {0};
        }
        cp_resp: coverpoint vif.cb.pslverr iff (vif.cb.psel && vif.cb.penable) {
            bins ok    = {0};
            bins error = {1};
        }
        // ── Cross coverage: addr × write × resp ───────────────
        addr_x_dir_x_resp: cross cp_addr, cp_write, cp_resp;
    endgroup
 
    function new(virtual apb_if vif);
        this.vif = vif;
        apb_cg = new();
    endfunction
endclass
 
// ── Why the clocking-block sampling event matters ─────────────
// Covergroup samples are evaluated when the @(vif.cb) event fires.
// At that instant, vif.cb.paddr, vif.cb.pwrite, vif.cb.pslverr all
// reflect their input-skew-sampled values — the same values the TB sees.
// Without the clocking block (e.g., @(posedge pclk)), the covergroup
// would sample in the Active region before NBA — potentially capturing
// pre-edge values, leading to coverage bins that don't match what
// actually happened on the bus.

The teaching: every cycle-sampled covergroup in a production VIP samples on a clocking block edge — not on the raw clock. The clocking block's input skew gives the coverage logic the same settled values the rest of the verification environment sees. Cycle-accurate coverage that disagrees with the rest of the TB log is almost always a covergroup sampled on the raw clock instead of the clocking block.

8.4 Multi-clock SoC — one clocking block per domain

Real SoCs have many clock domains. The standard architecture is one clocking block per domain, each in the interface for that domain:

SystemVerilog — multi-domain SoC: AXI + APB + DDR each with its own clocking block
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Fast core / AXI domain ────────────────────────────────────
interface axi_if (input logic aclk);     // aclk: 400 MHz
    // ... AXI signals ...
    clocking cb @(posedge aclk);
        default input #1step output #1;
        // ...
    endclocking
endinterface
 
// ── Slow peripheral / APB domain ──────────────────────────────
interface apb_if (input logic pclk);     // pclk: 100 MHz
    // ... APB signals ...
    clocking cb @(posedge pclk);
        default input #1step output #1;
        // ...
    endclocking
endinterface
 
// ── DDR controller domain ─────────────────────────────────────
interface ddr_if (input logic ddr_clk);  // ddr_clk: 800 MHz
    // ... DDR signals ...
    clocking cb @(posedge ddr_clk);
        default input #1step output #1;
        // ...
    endclocking
endinterface
 
// ── Always-on (AON) domain ────────────────────────────────────
interface aon_if (input logic aon_clk);  // aon_clk: 32 kHz
    // ... wakeup, reset control signals ...
    clocking cb @(posedge aon_clk);
        default input #1step output #1;
        // ...
    endclocking
endinterface
 
// ── Top-level wires everything together ───────────────────────
module tb_top;
    logic aclk = 0, pclk = 0, ddr_clk = 0, aon_clk = 0;
    always #1.25 aclk    = ~aclk;        // 400 MHz
    always #5    pclk    = ~pclk;        // 100 MHz
    always #0.625 ddr_clk = ~ddr_clk;    // 800 MHz
    always #15625 aon_clk = ~aon_clk;    // 32 kHz
 
    axi_if axi  (.aclk(aclk));
    apb_if apb  (.pclk(pclk));
    ddr_if ddr  (.ddr_clk(ddr_clk));
    aon_if aon  (.aon_clk(aon_clk));
 
    soc_dut dut (.axi(axi.DUT), .apb(apb.DUT), .ddr(ddr.DUT), .aon(aon.DUT));
 
    // Each agent has its own program / module, each with its own clocking block
    axi_master_test  axi_tb  (.axi(axi.MASTER));
    apb_master_test  apb_tb  (.apb(apb.TB));
    ddr_master_test  ddr_tb  (.ddr(ddr.TB));
    aon_pmu_test     aon_tb  (.aon(aon.TB));
endmodule

Architectural rules for multi-clock testbenches:

  • Every clock domain that the TB touches gets its own interface and its own clocking block.
  • Per-domain agents drive their own clocking blocks — never reach across into another domain's clocking block.
  • Signals that cross between domains use CDC primitives (next section) and are typically not in either domain's clocking block — they're handled by dedicated synchroniser monitors.
  • A "global" testbench top-level module orchestrates per-domain agents but does not host any clocking block of its own — it's the wiring layer.

8.5 Clock-Domain Crossing (CDC) — introduction

Two signals that originate in different clock domains and meet at a single flop are a clock-domain crossing. CDC is where most real silicon bugs hide; the verification discipline starts with the clocking block (which proves what each domain's TB sees) and then layers CDC-specific structures on top.

Why CDC is dangerous. A flip-flop has a setup-and-hold window around its clock edge. If a signal coming from a different clock domain transitions inside that window, the flop's output goes metastable — settling to either 0 or 1 eventually, but in an unbounded amount of time. Real silicon resolves metastability within a few hundred picoseconds; simulation models it as either an X-propagation or a non-deterministic 0/1.

The canonical CDC fix: the two-flip-flop synchroniser. Two flops in series in the destination domain absorb the metastable window — if the first flop goes metastable, the second flop has a full destination clock cycle to wait for it to settle before sampling.

Figure — two-flip-flop synchroniser absorbs metastability at a CDC boundary

12 cycles
Figure — two-flip-flop synchroniser absorbs metastability at a CDC boundarysrc_sig changes asynchronously to dst_clksrc_sig changes asynch…FF1 captures — may be metastable (X)FF1 captures — may be …FF2 samples FF1 → metastability resolved, dst_sync stableFF2 samples FF1 → meta…src_clksrc_sigdst_clkdst_meta (FF1)XXdst_sync (FF2)t0t1t2t3t4t5t6t7t8t9t10t11
Source-domain signal src_sig transitions asynchronously to the destination clock. dst_meta (first flop) captures the transition but may be metastable for the cycle. dst_sync (second flop) samples dst_meta one destination cycle later — by which time metastability has resolved. dst_sync is the safe, synchronised value the rest of the destination logic uses. The cost is one destination cycle of latency at the CDC boundary.

CDC verification layers built on clocking-block infrastructure:

LayerWhat it doesClocking-block role
Per-domain monitorsSample each side of the boundaryOne clocking block per domain — gives the monitor race-free post-NBA values
CDC assertionsVerify two-flop synchroniser is present; verify gray coding on FIFO pointersdefault clocking of the destination domain anchors the assertion's sampling event
Metastability injectionInject X for one cycle after asynchronous transitionsDisabled / overridden through the clocking block's input skew
Async FIFO verificationVerify full/empty flags computed correctly across domainsTwo clocking blocks (write-side cb, read-side cb) sample the gray-coded pointers on each side
CDC coverageCoverage on which CDC paths exercised, under what handshakeCovergroups sample on per-domain clocking blocks

The introductory takeaway: the clocking block isn't just the TB-DUT race fix. It's the per-domain timing primitive that makes every higher-level CDC verification structure expressible. A multi-clock SoC without one clocking block per domain is a multi-clock SoC where every monitor, every assertion, every covergroup is making implicit and inconsistent timing assumptions. A multi-clock SoC with per-domain clocking blocks has a single source of truth for "when does each side sample what."

A full CDC lesson — Gray-code pointers, async FIFOs, handshake protocols, the IEEE 1800 race-detect mechanism, JasperGold CDC formal flows — is a Module 17 / advanced-topics treatment. This deep-dive establishes the foundation.

9. Industry Usage — Where Clocking Blocks Land in Real Verification

  • UVM uvm_driver and uvm_monitor — every UVM agent's driver and monitor accesses DUT signals through a virtual interface's clocking block. The TLM transactions are decoded into clocking-block reads/writes; the timing discipline is uniform across all UVM agents in the testbench.
  • Every shipping AMBA / DDR / PCIe / Ethernet / USB / I²C VIP uses one clocking block per channel / per direction. The VIP's bus functional model is built on @(cb) waits and cb.signal <= value drives; tests using the VIP get race-free access without thinking about it.
  • Default clocking for assertion sets — SVA-heavy verification environments declare default clocking cb; at the interface level so every property in the interface inherits the same sampling discipline. Property libraries are written assuming this convention.
  • Covergroup sampling events — UVM uvm_subscriber-based coverage classes typically attach their covergroup @(vif.cb) to a clocking block edge. Per-cycle coverage that doesn't depend on a clocking block is non-portable across simulators.
  • Multi-clock SoC verification — every modern SoC TB has 5–50 clock-domain interfaces, each with its own clocking block. The architecture rule "one clocking block per domain, no cross-domain access" is taught in every major VIP coding standard (Synopsys, Cadence, Siemens).
  • CDC sign-off flows — formal CDC tools (JasperGold, Conformal LP, Onespin) consume the clocking-block declarations as their "this is the verification's notion of when each domain samples" reference. CDC bug reports cite the clocking block as the timing-discipline boundary.
  • Power-aware verification (UPF / CPF) — domains can be powered down independently; the clocking block for a powered-down domain stops generating its event. Power-aware UVM environments use the clocking block presence as a signal for "this domain is active."

10. Design Review Notes — What a Senior Will Flag

Pattern in the diffWhat review will say
interface bus_if (input logic clk); ... logic data; ... endinterface (no clocking block)"Every synchronous interface needs a clocking block. Without one, every agent that uses the interface reinvents the timing discipline — inconsistently. Add clocking cb @(posedge clk); default input #1step output #1; ... endclocking."
Module port output [7:0] data in DUT shown as output [7:0] data in the TB's clocking block"Wrong direction. DUT output = TB-side input (the TB reads what the DUT produces). The clocking block's direction is from the TB's perspective."
bus.cb.psel = 1; (blocking assignment)"Clocking block outputs require <= (non-blocking). The skew is implemented through NBA scheduling; blocking bypasses it. Some simulators reject this at compile time; others produce undefined behaviour."
Assertion property p; @(posedge pclk) ...; endproperty in an interface that has clocking cb @(posedge pclk)"Use @(cb) or default clocking cb; so the assertion samples the same values the TB sees. Raw @(posedge pclk) bypasses the clocking block's input skew and reads Active-region pre-NBA values — assertion can disagree with the TB."
covergroup cg @(posedge clk); coverpoint paddr; endgroup in a clocking-block-equipped interface"Sample on @(vif.cb) instead. Sampling on the raw clock means the covergroup captures pre-NBA values; the bin hits won't match what the TB log shows."
Multi-domain SoC TB with clocking cb @(posedge clk) reused across multiple domains"One clocking block per clock domain. Reusing a clocking block across aclk/pclk/ddr_clk is structurally impossible (a clocking block is bound to one clock); the bug is probably that multiple interfaces share the wrong clock port. Audit the interface-to-DUT wiring."
default input #1 output #1; (default skews without #1step)"#1step is the recommended default for inputs — it samples in the Postponed region of the previous time step, before any active-edge evaluation. #1 is acceptable for outputs (drives 1 unit after the edge for setup margin), but inputs should use #1step for full pre-edge safety."
Clocking block missing default input/output #... declaration with per-signal skews everywhere"Declare the default once at the top; override per-signal only when truly needed. Each per-signal skew is one more thing for the next engineer to verify; defaults are the contract."
Async signals (resets, interrupts) listed in the clocking block"Async signals belong outside the clocking block. Including them attaches a clock skew to a signal that's intentionally asynchronous — defeats the purpose. Drive resets via the modport's direct output, not via bus.cb.presetn <=."

The single highest-value rule: one clocking block per clock domain, used by every cycle-sampling consumer in that domain — TB code, SVA properties, covergroups. The discipline question at design review is "does every cycle-accurate operation in this domain go through the same clocking block?" — if not, the verification stack is inconsistent.

11. Debugging Guide — Real Failures, Real Fixes

1

SVA fails for valid handshakes; assertion sees pready=0 when TB sees pready=1

SVA-BYPASSES-CLOCKING-BLOCK
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface apb_if (input logic pclk);
    logic pready, /* ... */;
    clocking cb @(posedge pclk);
        default input #1step output #1;
        input pready;
    endclocking
    // ── BUG: property samples raw pclk, not the clocking block
    property handshake;
        @(posedge pclk) psel && penable |-> pready;
    endproperty
    assert property (handshake);
endinterface
Symptom
Assertion fires "handshake violation" on cycles where the TB log clearly shows pready=1 and the test passes the functional check. Coverage shows the bin hit. Same simulator, same seed, contradictory results across the verification stack.
Root Cause
The assertion samples on @(posedge pclk) — Active region, before NBA. At that instant pready still holds its pre-edge value (0). The TB reads through bus.cb.pready which is sampled at #1step in the previous time step's Postponed region — post-NBA-settled. The assertion and the TB disagree on what pready was.
Fix
Change the property to @(cb) psel && penable |-> pready; or add default clocking cb; at the interface level so every property inherits the clocking block. Now the assertion samples the same value the TB reads. Cross-simulator regression noise disappears.
2

Coverage shows zero bin hits for transactions that clearly happened

COVERGROUP-WRONG-SAMPLE-EVENT
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
covergroup apb_cg @(posedge pclk);   // BUG: sampled on raw clock
    cp_resp: coverpoint pslverr iff (psel && penable) {
        bins ok    = {0};
        bins error = {1};
    }
endgroup
Symptom
Test runs 1000 APB transactions including 50 with error responses. Coverage report shows cp_resp.error = 0 (no error bin hits) even though the assertion log shows pslverr asserted 50 times. Coverage closure stalled at 95%.
Root Cause
Same class of bug as #1, in coverage. @(posedge pclk) samples in Active region. At that instant, psel && penable may still hold pre-edge values; the iff guard fails; the bin sample is skipped. The covergroup's notion of "when did the transaction complete" is one delta off from the TB's notion.
Fix
Change to covergroup apb_cg @(vif.cb); and reference signals as vif.cb.psel, vif.cb.penable, vif.cb.pslverr. The covergroup now samples post-NBA-settled values; bin hits match the assertion log; coverage closes.
3

Clocking block output reaches DUT before its next sampling edge — DUT samples mid-flight value

OUTPUT-SKEW-TOO-SMALL
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clocking cb @(posedge pclk);
    default input #1step output #0;   // BUG: output skew = 0
    output psel, paddr;
endclocking
Symptom
Random transactions intermittently fail with the DUT reading half-applied stimulus — psel=1 but paddr still showing the previous value. Same TB code, same DUT, simulator-dependent reproducibility.
Root Cause
output #0 schedules the drive at the same time as the clock edge. The DUT samples its inputs at that same edge. Race: did the DUT sample paddr before or after the TB drove it? Tool-dependent.
Fix
Use the recommended default output #1 (one time unit after the edge). The DUT sees the new value with full setup time before its next sampling edge — no race. #0 output skew is only ever appropriate for purely asynchronous signals, which shouldn't be in a clocking block in the first place.
4

Two AXI agents on the same clock domain — one master, one slave — both write to the same wire

WRONG-MODPORT-PER-AGENT
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Both agents use the .MASTER modport — both drive awvalid
axi_master_test  master_tb (.axi(axi.MASTER));
axi_slave_test   slave_tb  (.axi(axi.MASTER));  // BUG: wrong modport
Symptom
awvalid shows X (multi-driver conflict) every cycle. Simulator warns about multiple drivers. Test fails to complete a single transaction. Looks like a DUT-output bug; it's actually a TB-wiring bug.
Root Cause
Both agents connect to axi.MASTER modport, which exposes the master-side clocking block where awvalid is an output. Both agents try to drive it; the wire shows X. The slave agent should connect to axi.SLAVE where awvalid is an input.
Fix
Wire the slave agent to axi.SLAVE: axi_slave_test slave_tb (.axi(axi.SLAVE));. The slave's clocking block sees awvalid as input (the master drives, the slave reads). The X-conflict disappears; the bus runs cleanly.
5

CDC test passes in simulation but silicon fails — metastability not modelled

NO-METASTABILITY-INJECTION
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Two-flop synchroniser in the DUT
always_ff @(posedge dst_clk) begin
    dst_meta <= src_sig;          // first flop
    dst_sync <= dst_meta;          // second flop
end
// TB simply observes dst_sync and assumes it's correct
Symptom
Simulation runs clean: every CDC transition results in a stable dst_sync exactly one destination cycle after dst_meta. Coverage looks complete. Silicon brings up first time but exhibits intermittent failures under specific PVT corners that the regression never caught.
Root Cause
Plain SystemVerilog simulation does not model metastability. When src_sig transitions during dst_clk's setup/hold window, the simulator deterministically captures the new value — but real silicon may take an unbounded time to resolve and may produce the old value on the second flop the next cycle. The simulation gives a falsely optimistic picture of CDC behaviour.
Fix
Use a CDC verification methodology that injects metastability. Standard approaches: (a) wrap the synchroniser in a model that randomly inserts a one-cycle X on dst_meta when src_sig transitions inside the destination setup window; (b) use formal CDC tools (JasperGold CDC, Conformal LP) that prove the synchroniser is correctly placed and that all CDC paths use proper synchronisation; (c) run gate-level simulation with SDF back-annotation — the SDF-derived setup/hold checks expose the race. The clocking block isn't the whole CDC story but it's the per-domain timing primitive every CDC structure builds on.

12. Interview Insights — What Interviewers Actually Probe

A clocking block is a SystemVerilog construct, declared inside an interface, that packages a clock edge with the signal directions and skews used to sample inputs and drive outputs. It solves three problems: (1) the TB-DUT race in the Active region (by sampling inputs at #1step in the previous Postponed region and driving outputs at #1 after the edge); (2) multi-clock-domain coordination (one clocking block per domain gives every consumer in that domain a consistent timing contract); (3) the SVA / coverage / CDC dependency chain (assertions, covergroups, and CDC monitors all anchor to the clocking block's sampling event so they see the same values the TB sees).

13. Exercises

1. Design — clocking block for AHB-Lite (Foundation)
Write an interface ahb_lite_if (input logic hclk); with a clocking block cb covering hsel, haddr, hwrite, hwdata, hready, hrdata, hresp. Use default input #1step output #1. Add a modport TB exposing cb and a modport DUT with the correct directions from the DUT's perspective.

2. Debug — the silent SVA failure (Intermediate)
A teammate's APB property property handshake; @(posedge pclk) psel && penable |-> pready; endproperty fires on cycles where the TB clearly logged pready=1. The interface has a clocking cb @(posedge pclk) with pready as an input. Identify the one-line fix and explain why the fix works.

3. Code review — multi-domain wiring (Intermediate)
A teammate's testbench wires an APB slave VIP using apb_slave_test slave (.apb(apb_bus.TB)); — the same modport as the master VIP. Coverage shows zero bin hits. Identify the architectural bug and propose the correct fix (hint: it involves a second clocking block + modport in the interface).

4. Trade-off — default clocking everywhere vs explicit @(cb) per property (Advanced)
A junior teammate argues: "always use default clocking at the interface — it's cleaner; never name the clocking block on individual properties." Argue the case against the blanket rule. When does an explicit @(cb) or @(posedge clk) on a specific property legitimately beat the default? Consider multi-clock interfaces, async assertions, and properties that need a different sampling discipline from the rest of the interface.

14. Summary

A clocking block is the per-clock timing contract at the centre of every modern SystemVerilog verification environment. It eliminates the TB-DUT race by sampling inputs at #1step before the edge and driving outputs at #1 after. It anchors the sampling event for SVA properties and functional coverage so the entire verification stack agrees on what each signal looked like at each edge. It scales to multi-clock SoCs via one clocking block per domain. It provides the per-domain timing primitive every CDC verification structure builds on.

Defaults to memorise. clocking cb @(posedge clk); default input #1step output #1; ...; endclocking; is the canonical declaration. Direction is from the TB's perspective. Outputs must use <= (non-blocking). Use default clocking cb; at the scope where properties / covergroups live so they inherit the same sampling event. One clocking block per clock domain; never reuse across domains. Async signals (resets, interrupts) live outside the clocking block.

This deep-dive completes Module 15.2. The companion introductory page at /learnings/systemverilog/clocking-blocks covers the basics. Next up: 15.3 — Input & Output Skews — the granular control over per-signal sampling and driving timing, including the #1step, fractional-cycle, and zero-skew use cases the deep-dive only sketched.