Introduction to SystemVerilog Clocking Blocks

 How do I use SystemVerilog clocking blocks in VCS?  

What are the delays between the Design Under Test and the testbench?

Answer:

Clocking blocks are used to ensure that a Testbench (TB), in a program block, communicates synchronously with the Design Under Test (DUT). The TB and DUT should be connected with an interface, and the clocking block should be put into the interface.

Start with a simple D-flip flop with a d input and q output (both synchronous). It uses the following interface:

interface dff_if (input bit clk);
  logic q, d;

  clocking cb @(posedge clk);
    input q;                  // TB input
    output d;                 // TB output
  endclocking

  modport DUT (input clk,     // Design under test 
               input d,
               output q);
  modport TB  (clocking cb);  // Synch signals
endinterface: dff_if 
This has two modports, one for the DUT (the dff) and one for the TB. The clocking block is only used by the TB.

How does the testbench drive a signal in the design?

When a TB changes a signal, the result is not seen immediately in the DUT. In the TB, you can drive the d line with:

  dff_if.cb.d <= 0;
This is a non-blocking assignment (NBA). This is because the program block executes in a different timing region than the design, so the value of d will not propagate immediately to the DUT, with or without a delay. This is a synchronous assignment (note the .cb. in the name) so d will not be updated until all TB statements in the program have completed for this time slot, and then not until the next posedge of the clock. Even if the statement is executed at the same time as the clock goes high, the DUT will not latch this new value as the clock has already gone high. This helps reduce race conditions.

Even if you use a hierarchical reference to drive a DUT signal directly, bypassing the interface, you still need to use an NBA.

You can advance time by adding a cycle delay to the assignment:

  ##1 dff_if.cb.d <= 0;
Now the d signal will change one clock cycle from now. But since the NBA is in the TB, it will happen after the DUT executes, and after the clock has already changed. So the new value of d will not be seen for another cycle.

What happens when a TB tries to sample a signal from the DUT?

If you read a signal using a clocking block, SystemVerilog acts as if there is a synchronizer inserted into the line. The following statement:

gnt_out = dff_if.cb.q;
will get the value of q from the previous clock cycle. For example, if the above line executes in the same time slot when the clock goes high, you will get the value from the previous clock edge.

How long is the minimum delay from the testbench, through the design, back to the testbench?

It takes one cycle for the output of the TB to be clocked into the DFF, and then one cycle for the output to propagate back into the TB.

Here is the code used in the above example. You can compile it with:

   vcs -sverilog -R example.sv
Example code
// example.sv
`timescale 1ns/1ns

interface dff_ifc(input bit clock); 
  logic q, d; 

  clocking cb @(posedge clock); 
    output d; 
    input q; 
  endclocking

  modport DUT (input d,
               output q);

  modport TB (clocking cb);

endinterface


module dff (dff_ifc.DUT dff_if, input bit clock);
  always @(posedge clock)
    begin
      dff_if.q = dff_if.d;
      $display("@%0d: DUT: q=%b", $time, dff_if.q);
    end
endmodule



program automatic test (dff_ifc.TB dff_if);

initial
  #10 repeat (100)
    #1 $display("@%0d: TB:  q=%b", $time, dff_if.cb.q);

initial begin
  ##2 dff_if.cb.d <= 0;
  $display("@%0d: TB:  drive d=0", $time);
  ##2 dff_if.cb.d <= 1;
  $display("@%0d: TB:  drive d=0", $time);
  ##2 dff_if.cb.d <= 0;
  $display("@%0d: TB:  drive d=0", $time);
  ##2 dff_if.cb.d <= 0;
  $display("@%0d: TB:  drive d=0", $time);
  ##2 dff_if.cb.d <= 0;
  $display("@%0d: TB:  drive d=0", $time);
  $finish;
end
endprogram



module top;
  bit  clock = 1;
  always #5 clock = !clock; 

  dff_ifc dff_if(clock); 
  dff a1 (dff_if, clock);
  test t1(dff_if);

  initial 
    $monitor("@%0d: Top: d=%b, q=%b, clock=%b", $time, dff_if.d, dff_if.q, clock);
  
endmodule

The program block has the "automatic" modifier so that all blocks and routines inside will use automatic storage. This is not needed for this example, but is a recommended coding style.

Comments

Popular posts from this blog

Design a clock divide-by-3 circuit with 50% duty cycle

Memory power reduction

SVA