Why not use program block

Hi,
After taking a glance at the ovm examples, I noticed that both the DUT and testbench are defined in modules, would it make some timing problems? Why not separate the testbench from the design by using the systemverilog program block?

scofield

Let me turn the question around: Why should anyone use a program block for their testbench, if everything works fine without them?

The developers of the AVM and OVM do not believe that program block solve timing problems on their own. Numerous methodologies already in use by RTL designers that eliminate races are sufficient for testbenches.

Dave

I’m not sure it matters if program blocks are worthwhile from an OVM perspective? With the program block clarification going into the 2008 draft, all that is needed is for the ‘do_test’ call to be done inside a program block. All the testbench threads spawned from there will be scheduled within the reactive region. Simulators that support program blocks nested in modules make this a 2 line change.

Does anything with OVM conflict with doing this?

But I agree with Dave_59, it seems like a special case to rely on a program block semantics for correctness (the ovm examples do not, so they do not need program blocks). Good verilog design avoids the timing problems in the majority of cases.

Still, I’ve noticed that some of the SV books on the market make a big deal out of program blocks, so it can seem odd to see them ignored totally in the OVM examples …

Program blocks came directly from the Vera donation, and try to mimic the scheduling semantics that a PLI application has interacting with a Verilog simulator. So coming from a Vera background, program blocks make perfect sense and do help people transitioning fro Vera to SV. But looking at SV from scratch, they are extra language baggage.

If you look at other languages like SystemC and VHDL; they don’t have these scheduling semantics and I think it will create problems as people try to mix IP from different language sources.

Also, if you start thinking about un-timed transaction level models(TLM) where both testbench and DUT are using some TLM abstractions, I think you will run into problems in the way that “delta” cycles are constituted. The active and re-active regions will be out of sync.

Dave

Thanks dave and jgg.

Hi all,

I faced sampling issues on using clocking blocks without program block while simulating my code in IUS. The same thing works well in QUESTASIM.It works even with IUS , if I add a #0 delay before sampling the input signals of the clocking block or instantiate my classes in a program block.

I have a scenario where Master drives request at one posedge of clock and expects ready from slave after any posedge of clock after the current one. My code does not work properly if both Master and Slave drive response and ready signal at the same time(using clocking block).
Please refer to the example below, where I have tried to recreate the same issue.

interface temp_if(input clk);
  logic request;
  logic response;

  clocking master_cb @(posedge clk);
    input response;   
    output request;
  endclocking
  
   clocking slave_cb @(posedge clk);
    output response;   
    input request;
  endclocking
  
  modport master_if (input clk, clocking master_cb);
  modport slave_if (input clk, clocking slave_cb);
  
endinterface

//program test(temp_if intf);
module top;
class master;

  virtual temp_if.master_if mst_intf_inst; 

  function new(virtual temp_if.master_if mst_intf_inst);
    this.mst_intf_inst = mst_intf_inst;
  endfunction

  task run();
    mst_intf_inst.master_cb.request <= 1'b0;
    repeat(5) begin
      @(posedge mst_intf_inst.clk);
    end
    repeat(5)begin 
      mst_intf_inst.master_cb.request <= 1'b1;
      @(posedge mst_intf_inst.clk);
      `ifdef HASH_0
      #0;
      `endif
      if(mst_intf_inst.master_cb.response === 1'b1)begin
        $display($time, ": master received RESPONSE on next clock as expected");
      end
      else begin
        $display($time,": ERROR: master did not received RESPONSE on next clock as expected");
      end
      mst_intf_inst.master_cb.request <= 1'b0;
      @(posedge mst_intf_inst.clk);
      end
    
  endtask  
endclass

class slave;

  virtual temp_if.slave_if slv_intf_inst; 

  function new(virtual temp_if.slave_if slv_intf_inst);
    this.slv_intf_inst = slv_intf_inst;
  endfunction
  
  task run();
    slv_intf_inst.slave_cb.response <= 1'b0;
    repeat(5) begin
      @(posedge slv_intf_inst.clk);
    end
    repeat(5)begin 
      slv_intf_inst.slave_cb.response <= 1'b1;
      @(posedge slv_intf_inst.clk);
      `ifdef HASH_0
      #0;
      `endif
      if(slv_intf_inst.slave_cb.request === 1'b1)begin
        $display($time,": Slave received REQUEST on next clock as expected");
      end
      else begin
        $display($time,": ERROR: Slave did not received REQUEST on next clock as expected");
      end
      slv_intf_inst.slave_cb.response <= 1'b0;
      @(posedge slv_intf_inst.clk);
      end
    
  endtask  
endclass

  bit clk = 1'b1;
  
  always begin
    #10 clk = ~clk;
  end
  temp_if intf(clk);
  master m1;
  slave s1;
  initial begin

    $dumpvars();
    m1 = new(intf);
    s1 = new(intf);
    fork 
      m1.run();
      s1.run();
    join
    $finish;
  end

//endprogram

//module top();

  //test t1(intf);
  
endmodule

As per me as section 14- Scheduling Semantics of SV LRM, this issue should not have come up with/without the program block.
Please let me know if it is some kind of bug in IUS or some misunderstanding of the above section in the LRM.
Regards,
Amrutha

Amrutha,

This works in Questa, but there is a race between the update of the sampled registers and the delay control:

@(posedge mst_intf_inst.clk);
You should use the clocking block event to time your master and slave run threads, and the sample updated is guaranteed to occur before the clocking block event.
@(mst_intf_inst.master_cb);

Dave Rich

Amrutha,
This works in Questa, but there is a race between the update of the sampled registers and the delay control:
@(posedge mst_intf_inst.clk);
You should use the clocking block event to time your master and slave run threads, and the sample updated is guaranteed to occur before the clocking block event.
@(mst_intf_inst.master_cb);

Dave Rich

Hi Dave,
I have a problem. Can you explain what’s the difference between @(posedge mst_intf_inst.clk) and @(mst_intf_inst.master_cb) ? There’s a description for the clocking event in the IEEE std 1800-2005, section 15.9 – Clocking block events:

clocking dram @(posedge phil);
inout data;
output negedge #1 address;
endclocking

The clocking event of the dram clocking block can be used to wait for that particular event.
@(dram);
The above statement is equivalent to @(posedge phil).

The 1800-2009 std now says

Upon processing its specified clocking event, a clocking block shall update its sampled values before triggering the event associated with the clocking block name.

The LRM text for clocking and program blocks has significantly changed in 1800-2009. You can se these changes in the latest draft availible from the IEEE store, or you can see the individual editing changes in the Mantis database. (guest/guest)

The 1800-2009 std now says
The LRM text for clocking and program blocks has significantly changed in 1800-2009. You can se these changes in the latest draft availible from the IEEE store, or you can see the individual editing changes in the Mantis database. (guest/guest)

So, let’s go back to Amrutha’s issue: if we use @(posedge mst_intf_inst.clk), then the later events will be implemented consecutively at the current timeslot before the nonblocking assignment is implemented, because the value of mst_intf_inst.clk has been updated to 1’b1; but if we use @(mst_intf_inst.master_cb), the clocking block value is sampled before the clk posedge, so the thread will be block until next timeslot. Right?