Jump to content

Parameterized Virtual Interface


Recommended Posts

Hello everyone, first post here :)

I have a problem when using parameterized interfaces. My current solution is not very pretty, so I will appreciate your suggestions.

I have a parameterized interface (and ofcourse a corresponding parameterized virtual interface). The problem is that in every place I create an instance of the vif, I must supply the parameters which forces me to make those classes (driver, monitor etc...) parameterized as well. I pretty much end with almost all classes being parameterized (since the agent must supply the parameters to the instances of the driver and so on). This is ofcourse not very elegant.

Do you know of a good way to overcome this limitation? I can't use `define to pass the parameters since the idea is to later create multiple instances of the same agent with different parameters.

Thanks for your help!

Link to comment
Share on other sites

Hi,

I wrote an article on this a while back with a simple generic coding example using pure SV1800 code.

Maximizing Reuse of Parameterized Interfaces within a Testbench

However, personally I do not feel there is a truly elegant solution using just pure SV1800-2009 code.

You can do some tricks like simplify the coding of the virtual interface by embedding it within the interface.

interface bus_if #( BUS_WIDTH= `MAX_ADDR_SIZE ) ( input logic clock  );
  logic [bUS_WIDTH:0] addr;
  typedef virtual bus_if #(.BUS_WIDTH(BUS_WIDTH)) vif_t;
endinterface // bus_if

Somewhere, somehow you need to get the object-specialization into the object which wants to use it.

adiel@synopsys.com

Link to comment
Share on other sites

Perhaps I'm missing something, but I can't see how using typedef helps in case of multiple instances with different parameters. Wouldn't it cause en error declaring the same typedef twice with different values?

interface bus_if #( BUS_WIDTH= `MAX_ADDR_SIZE ) ( input logic clock  );
   logic [BUS_WIDTH:0] addr;
   typedef virtual bus_if #(.BUS_WIDTH(BUS_WIDTH)) vif_t;
endinterface // bus_if

module tb
bus_if #(16) if1(clk);
bus_if #(32) if2(clk);
...
endmodule
Link to comment
Share on other sites

oops, my bad.

The typedefs are indeed scoped, but I still need to parameterized all of the classes that require an instance of the virtual interface, except this time I'll need to give it a 'type' parameter of that virtual interface.

I guess like previously mentioned, there is no elegant solution here...

Link to comment
Share on other sites

I've run into this before myself, so I'll take a stab at it. I believe what you're looking for is polymorphic behavior from the virtual interface: There is some set of behaviors that all parametrized versions of bus_if share, and you want your driver, monitor, etc. classes to be able to use these common behaviors without caring about BUS_WIDTH. That way you don't have to parametrize your classes.

Of course, interfaces are not classes and cannot use inheritance and polymorphism. What you can do, though, is to make a wrapper class for your interface and manually abstract the behaviors you want into the wrapper.

Here is an example:

`define MAX_ADDR_SIZE 64

interface bus_if #( BUS_WIDTH= `MAX_ADDR_SIZE ) ( input logic clock  );
   logic [BUS_WIDTH-1:0] addr;
endinterface // bus_if

virtual class bus_if_wrapper;
   pure virtual function int get_width();
   pure virtual function logic [`MAX_ADDR_SIZE-1:0] get_addr();
endclass

class bus_if_param_wrapper #(BUS_WIDTH=`MAX_ADDR_SIZE) extends bus_if_wrapper;
   virtual         bus_if #(BUS_WIDTH) intf;

   function new(virtual bus_if #(BUS_WIDTH) intf_);
      super.new();
      intf = intf_;
   endfunction
   
   virtual         function int get_width();
      return BUS_WIDTH;
   endfunction

   virtual         function logic [`MAX_ADDR_SIZE-1:0] get_addr();
      return {{`MAX_ADDR_SIZE-BUS_WIDTH{1'b0}}, intf.addr};
   endfunction
endclass

module top;
   wire clk;
   
   bus_if #(32) if32 (clk);
   bus_if #(64) if64 (clk);

   bus_if_param_wrapper #(32) wrapper32 = new(if32);
   bus_if_param_wrapper #(64) wrapper64 = new(if64);
   
   initial begin
      bus_if_wrapper wrappers [] = '{wrapper32, wrapper64};

      foreach (wrappers[i]) begin
         $display("width=%d, addr = %x\n", wrappers[i].get_width(), wrappers[i].get_addr());
      end
   end
endmodule

Here the behaviors I abstracted are the ability to get the bus width and to get the address from the interface, zero-padded to the maximum address width in our system.

Of course, if the behaviors that you want to access from your classes do depend on BUS_WIDTH, then you have no choice but to parametrize those classes.

Link to comment
Share on other sites

There is a DVCon 2011 paper titled "Parameters and OVM -- Can't They Just Get Along?" that addresses this issue in sections 6 and 7 (I didn't write it nor have anything to do with it). Hopefully, the link will take you to the 2011 proceedings archive, and this paper is under the UVM Applications session.

The highlight is that you can greatly simplify maintenance of all these parameters up and down your class hierarchy by using some macros defined in a file that you include anywhere you require the parameter declarations or usage. The macros would provide symbols for the parameter declaration and the parameter mapping. So, you could have

// -- File:  params_defines.svh:
`ifndef __PARAMS_DEFINES_SVH__
`define __PARAMS_DEFINES_SVH__
`define params_declare #(BUS_WIDTH = 32)
`define params_map #(.BUS_WIDTH(BUS_WIDTH))
`endif

Your interface and TB could look like

`include "params_defines.svh"
interface bus_if `params_declare (input logic clock);
  logic [BUS_WIDTH-1:0] addr;
endinterface : bus_if

module tb;
  bus_if #(16) if1(clk);
  bus_if #(32) if2(clk);
endmodule : tb

Your monitor, for example, could look like

`include "params_defines.svh"
class my_monitor `params_declare extends uvm_monitor;
  `uvm_component_param_utils(my_monitor`params_map)
  virtual bus_if`params_map vif;
  // -- Skipping rest of monitor...
endclass : my_monitor

Depending on how you set up the defines file, and use it throughout the class hierarchy in your testbench, you only need to maintain one file as opposed to many.

Link to comment
Share on other sites

Thanks for the replies!

jeff.schroeder

Using a class wrapper doesn't help, since the class it self is parameterized. Instanciating it in a monitor for example (and then using the config_db to get it) still requires passing the parameters.

mea1201

I'm currently using something similar to what you suggest:

`define my_monitor my_monitor#(DATA_WIDTH)

Can't say I'm satisfied though, but it is a bit more elegant

Link to comment
Share on other sites

Dudi,

If the API required to talk to the interface from the driver/monitor needs parametrization, like is the data bus 32-or 64 bits, there's no getting around the fact that both the driver and interface need to be fed the same parameters. However, if the communication between the driver and interface is independent of those parameters, there is a more elegant solution. Don't use virtual interfaces and stick with abstract classes. There are a number of papers describing this method starting with one I co-authored with Jonathon Bromley at DVCon 2008: "Abstract BFMs Outshine Virtual Interfaces for Advanced SystemVerilog Testbenches"

Also Dvcon 2010: 6.1Coverage Driven Verification of an Unmodified DUT within an OVM Testbench

And DVCon 2011: 1.4First Reports from the UVM Trenches: User-friendly, Versatile and Malleable, or Just the Emperor's New Methodology?

Link to comment
Share on other sites

If you need the interface to be parameterized, you can avoid parameterization of the rest of the environment by using the factory. Give your parameterized driver a non-parameterized base class and a non-parameterized item so the agent can instantiate it. Then use the factory to override the driver instance with the correct parameterized version. That way the parameterization is kept to a minimum.

Link to comment
Share on other sites

It isn't elegant, but I've overcome situations like this by creating a non-parameterized port handler class to define empty virtual methods to manipulate the interface. Then I create a parameterized extension of that class which implements those virtual methods to actually do stuff with the parameterized interface.

The non-parameterized driver and monitor then create the non-parameterized port handler class and access the interface through those virtual methods. Then the port handler class can be replaced using the factory API with a correctly parameterized port handler class.

Edited by pratta
technical correction
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...