3

I have a UVM testbench for a small block in my chip. In this there is an agent with a driver that drives data on a virtual interface which looks something like this:

interface my_if (input bit clk);

  logic [3:0] opcode;

  // Clocking block for the driver
  clocking drvClk @(posedge clk);
    output opcode;
  endclocking

  // Clocking block for the monitor      
  clocking monClk @(posedge clk);
    input opcode;
  endclocking

endinterface

I use this interface in my driver like this:

class my_driver extends uvm_driver #(my_tr);
  my_if vif;
  ...
  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);

    forever begin
      seq_item_port.get_next_item(req);

      // Drive the transaction onto the interface
      // and wait for next clock
      vif.opcode <= req.opcode;
      @(vif.drvClk);

      seq_item_port.item_done();
    end
  endtask
endclass

As far as i can tell, this is the recommended way of doing things, and it works well. The problem arises when I integrate this agent into a higher level testbench. In that case the agent is now passive and the driver is not built. I am assigning the opcode value to the interface so the monitor can observe it. Here is a snippet of my top level wire harness:

module my_top();
  bit clk = 0;

  always #5 clk = !clk;

  // instantiate the interface
  my_if my_if_inst(.clk(clk));

  // instantiate my dut
  my_dut dut(...);

  // pull out the internal opcode signal and assign it
  // to the interface
  assign my_if_inst.opcode = dut.submodule.opcode;

  // Set the virtual interface inside the agent
  initial begin
    uvm_config_db#(virtual my_if)::set(uvm_root::get(),"uvm_test_top.tb.env.my_agent", "vif", my_if_inst);
  end 
endmodule

When I run this in NC I get a warning:

ncelab: *W,ICPAVW: Illegal combination of driver and procedural assignment to variable opcode detected (output clockvar found in clocking block)

This makes sense since the interface defines this signal as an output for the drvClk block and I am doing an assignment at the top level. I can just ignore this warning (the code works just fine) but I would rather code it in a way that it runs cleanly. What is the recommended way to do this? I got rid of the clocking block for the driver and that works, but I think I am setting myself up for race conditions if I do that.

nguthrie
  • 2,615
  • 6
  • 26
  • 43
  • I happened to encounter a similar issue. Surprisingly VCS did not have any issues compiling the same code but Incisive did. @dave_59's answer is accurate. There's an override [switch](https://community.cadence.com/cadence_technology_forums/f/functional-verification/40909/elaboration-error-icdpav-on-xcelium) in Incisive for this though, not sure why – Rajesh Shashi Kumar Jul 02 '19 at 06:11

1 Answers1

8

Easy; make opcode a wire in your interface.

Treat opcode the same as you would a bidirectional signal. See my DVCon paper on this subject.

dave_59
  • 39,096
  • 3
  • 24
  • 63
  • Well, that did the trick. I had just copied the examples which used 'logic' for everything and never questioned it. I assumed that since I was doing a non-blocking assignment in the driver that a wire wouldn't be legal. In my head "<=" means "you must have logic or reg here!". Thanks for making my first stackoverflow experience a quick one! – nguthrie Oct 08 '13 at 19:37
  • Thanks Dave, The document makes sense. However I've a problem at reset condition, generally driver is written to drive interface signals instead of clocking block signals at reset. How do I tackle reset condition in driver? – wisemonkey Jan 02 '15 at 22:36
  • I'm trying to avoid declaring _reg and then assigning signal = signal_reg, is there any other/better way to do this? (can't edit comment after 5 minutes so a new comment) – wisemonkey Jan 02 '15 at 22:42
  • There's no need to drive signals asynchronously for the general testcase at reset. You may want to build s special testcase to handle asynchronous timing issues. – dave_59 Jan 05 '15 at 21:54