SystemVerilog Clocking Blocks

The clocking block provides a means of specifying the timing of synchronous signals relative to their clock. It defines the timing that the testbench will use to sample outputs from the DUT and drive inputs towards the DUT. A clocking block can only be declared inside a module, program, interface or checker.

The complete syntax can be found in the SystemVerilog specification ( which also contains a lot of information about their use. Lets skip to a simple example:

  1. clocking cb_axi @(posedge aclk);
  2.  default input #1ns output #2ns;
  3.  input tready;
  4.  output tvalid;
  5.  output tdata;
  6. endclocking

Listing 1

The above listing shows the declaration and instance of a clocking block named “cb_axi” (strictly speaking, cb_axi is the clocking identifier). Note that clocking blocks are implicitly instantiated so there are no separate declaration and instance.

The clock associated with this clocking block is aclk and it uses the rising edge, so the clocking event for this clocking block is @(posedge aclk).

The second line in the clocking block declaration defines the clocking skew times. The input clocking signals declared inside the clocking block above are all sampled 1ns before the clocking event. The output clocking signals are driven with a delay of 2ns after the clocking event. Note also that there no widths indicated for the clocking signals – the clocking blocks cannot assign values so widths are not needed.

Figure 1: Input and output skew timing for cb_axi clocking block

So how do we connect the clocking block to the DUT? In a schematic form, it looks like this:

Figure 2: Clocking block and DUT connections

Note how the clocking block inputs are outputs from the DUT and vice-versa.

An (incomplete) example SystemVerilog testbench for the above diagram would look something like this:

  1. /*-----------------------------
  2.   Signals
  3. ------------------------------*/
  4. logic tready;
  5. logic tvalid;
  6. logic [31:0] tdata;
  7. logic aclk;
  8. /*-----------------------------
  9.   Test driver
  10.   Accesses clkvars, not clocking signals
  11. ------------------------------*/
  12. initial begin
  13.  cb_axi.tvalid <= 1;
  14.  cb_axi.tdata <= 32’h01234567;
  15.  do begin
  16.   @(cb_axi);   // wait for cb_axi clocking event
  17.  end while (cb_axi.tready == 0);
  18. end
  19. /*-----------------------------
  20.   Clocking block
  21. ------------------------------*/
  22. clocking cb_axi @(posedge aclk);
  23.  default input #1ns output #2ns;
  24.  input tready;
  25.  output tvalid;
  26.  output tdata;
  27. endclocking
  28. /*-----------------------------
  29.   Instantiate DUT
  30. ------------------------------*/
  31. axi_periph UUT (
  32.  .ACLK    (aclk),
  33.  .TREADY  (tready),
  34.  .TVALID  (tvalid),
  35.  .TVALID  (tdata)
  36. );

Listing 2

For every clocking signal, there is a corresponding clockvar. A testbench which uses a clocking block should always sample or drive the clockvar, not the corresponding clocking signal. By default, the clockvar name is the same as the corresponding clocking signal. The clockvars are accessed using a dot notation which is made up from the clocking identifier and the clockvar name:

  1.  cb_axi.tvalid <= 1;

Listing 3

Defaults skews

A clocking block like this one that has no definition of skew values is legal and the skews used will be 1step for inputs and zero for the outputs:

  1. clocking cb_axi @(posedge aclk);
  2.  input tready;
  3.  output tvalid;
  4.  output tdata;
  5. endclocking

Listing 4

#1step input skew

The 1step is a time unit that denotes a negative time delay before the clocking event such that sampling will occur just before the clocking event. The sampling occurs after all other activity in the current time slot has finished – i.e. in the postponed region of the current time slot.

Setting skew values for individual clocking signals

It is possible to set different skew values for each clocking signal rather use a default value for all of them. In this example the default input skew is #1step and the default output skew is 1 ns, but tvalid and tdata both have their own individual skew values:

  1. clocking cb_axi @(posedge aclk);
  2.  default input #1step output #1ns;
  3.  input tready;
  4.  output tlast;
  5.  output #2ns tvalid;
  6.  output #4ns tdata;
  7. endclocking

Listing 5

More complex clocking events

Any valid event expression can be used as the clocking event. For example, a gated clock can be emulated like this:

  1. clocking cb_gated @(negedge clk iff master_enable);
  2.  default input #1step output #2ns;
  3.  input wrdata;
  4.  output rddata;
  5. endclocking

Listing 6

The iff statement is a qualifier for the @ event control. The event expression (negedge clk) will only trigger if the expression after iff is true. So the above clocking block will only sample inputs and drive outputs when there is a falling edge on clk and master_enable is true.

Double Data rate (DDR) clock

So far, all of the examples given have explicitly stated if the clocking block uses either the rising edge (posedge) or falling edge (negedge) of its associated clocking event. It may be necessary to specify a double-data rate (DDR) clock where both edges are used. The SystemVeriog specification comes to our rescue here as it contains two examples of how this can be done. The first uses two separate clocking blocks:

  1. reg j;
  2. clocking pe @(posedge clk);
  3.  output j;
  4. endclocking
  5. clocking ne @(negedge clk);
  6.  output j;
  7. endclocking

Listing 7

Here, the variable j will have a value that has been assigned by the last clocking block to have been activated by its clocking event. The second example is equivalent to the first, but uses only one clocking block which does not specify posedge or negedge for the clocking event:

  1. reg j;
  2. clocking e @(edge clk);
  3.  output j;
  4. endclocking

Listing 8

Default clocking block

One, and only one, clocking block inside any module, program, interface or checker can be defined as the default clocking clock:

  1. default clocking cb_gated @(negedge clk iff master_enable);
  2.  default input #1step output #2ns;
  3.  input wrdata;
  4.  output rddata;
  5. endclocking

Listing 9

When you use the ## delay operator which defines delays in terms of a certain number of clocking events, the clocking events used for the delay will be that defined in the default clocking block.

Using Clocking Blocks With Interfaces

A SystemVerilog interface can contain one or more clocking blocks which can help to simplify the interface timing in simulation. The clocking signals will be sampled or driven relative to the clocking event of the clocking block:

  1. interface axis (input logic aclk);
  2.  logic [15:0] tdata;
  3.  logic tvalid;
  4.  logic tready;
  5.  // clocking block for AXI Stream master
  6.  clocking cb_axis_mst @(posedge aclk);
  7.   default input #1step output #3ns;
  8.   output tdata;
  9.   output tvalid;
  10.   input tready;
  11.  endclocking
  12.  // clocking block for AXI Stream slave
  13.  clocking cb_axis_slv @(posedge aclk);
  14.   default input #1step output #2ns;
  15.   input tdata;
  16.   input tvalid;
  17.   output tready;
  18.  endclocking
  19.  // AXI stream master modport - for testbench only
  20.  modport tb_axis_mst_mp(clocking cb_axis_mst,
  21.   output tdata,
  22.   output tvalid,
  23.   input tready );
  24.  // AXI stream slave modport - for testbench only
  25.  modport tb_axis_slv_mp(clocking cb_axis_slv,
  26.   input tdata,
  27.   input tvalid,
  28.   output tready );
  29.  // AXI stream master modport - for synthesis
  30.  modport axis_mst_mp(
  31.   output tdata,
  32.   output tvalid,
  33.   input tready );
  34.  // AXI stream slave modport - for synthesis
  35.  modport axis_slv_mp(
  36.   input tdata,
  37.   input tvalid,
  38.   output tready);
  39. endinterface: axis

Listing 10

Note how there are two clocking blocks needed because we have one for the AXI Streaming master direction and one for the slave direction. Then there are four modports:

  • axis_mst_mp – master, no clocking block, for synthesis
  • axis_slv_mp – slave, no clocking block, for synthesis
  • tb_axis_mst_mp – master, includes clocking block, only used in testbench
  • tb_axis_slv_mp – slave, includes clocking block, only used in testbench

A complete example showing the use of clocking blocks with interfaces and modports can be downloaded.