Assigning elements of an virtual interface array?

Hi All,

In my OVM env of sequence based stimulus appraoch, I am using a bfm - module wrapper (based on the approach http://ovmworld.org/forums/showthread.php?t=109).

In the test bench, I have used “generate loop” to have multiple instances of the bfm wrapper.

I have to assign the elements of virtual interface array at the test bench using the system verilog interface handles from the multiple instances of the generate loop.

I will be using “set_config_object” to pass this virtual interface array down the hieararchy.

As it is illegal to access generated instance with a variable index, I want to know is there any other way exists to assign it?:confused:

in the testbench:
//array to hold interface handles
virtual sysif usysif[`NUM];
genvar I;
generate
for (I = 0; I < `NUM; I++)
begin : bfms
wrap uwrap(*);
end
endgenerate
//Illegal:mad:
for (int k=0;k<`NUM;k++)
usysif[k] = bfms[k].uwrap.lsysif;

Thanks for your inputs in advance:)

Hi TVAR,

SystemVerilog does not allow you to access an element of a generated block using a variable index. I don’t know of a “nice” solution to this problem other than assigning each interface explicitly, i.e.

usysif[0] = bfms[0].uwrap.lsysif;
usysif[1] = bfms[1].uwrap.lsysif;
usysif[2] = bfms[2].uwrap.lsysif;
usysif[3] = bfms[3].uwrap.lsysif;
...

If you have several related interfaces to connect, a macro may make the code a bit more manageable. Unfortunately, there is no `repeat in SystemVerilog so you will still require one statement for each connected interface.

Neither of these approaches are very flexible. If you have a lot of interfaces to connect and the number of interfaces is variable, you would probably be better off writing a simple perl script to generate that part of your code instead!

Regards,
Dave

HI Dave,

Thanks for your reply.:)

As I have been using compiler directive (eg: `define NUM 10) to generate the bfm wrappers, I CANNOT have individual interface assigments explicitly.

Also verilog doesn’t allow to access an element of a generated block using a variable index like::confused:

usysif[0] = bfms[0].uwrap.lsysif;

Eventually I had tried a solution using generate-assign statement pair as follows:

genvar K;
  generate
    for (K = 0; K < `NUM; K++)
      begin : VI
      assign  usysif[K] =  bfms[K].uwrap.lsysif;
      end
  endgenerate

It worked really well & able to access the virtual interfaces individually from usysif array.

But in QuestaSim 6.3e, I am getting the following warning::(

" Warning: /path/: [SVCHK] - Some checking for conflicts with continuous assignments to variables not yet supported. Run vopt to provide additional design-level checks."**

Even am getting this warning after running with vopt.:mad:

Could you please suggest me on the same?

TVAR

in the testbench:
//array to hold interface handles
virtual sysif usysif[`NUM];
genvar I;
generate
for (I = 0; I < `NUM; I++)
begin : bfms
wrap uwrap(*);
end
endgenerate
//Illegal:mad:
for (int k=0;k<`NUM;k++)
usysif[k] = bfms[k].uwrap.lsysif;

Hi,

Try this approach.

in the testbench:

//array to hold interface handles
virtual sysif usysif[`NUM];

genvar I;
generate
for (I = 0; I < `NUM; I++)
begin : bfms
wrap uwrap(*);
 
usysif[I] = uwrap.lsysif; //////////////// Try this ////////////////

end
endgenerate

I do have other idea:cool:,to be tried, integating legacy BFM to SV environment. First just check if above works for you:confused:.

Thanks,
Saurabh

hi saurabh,

Thanks for your help.:)

I have already tried ur suggestion.

It doesn’t allow to make such an assignment.

I do use a very good bfm wrapping technique, but unable to extend it for multiple bfm instances.:mad:

Can you suggest your wrapping technique?

I will try the same.

Regards
TVAR

Hi TVAR,

Hmm, a continuous assignment to a virtual interface in the generate block!!! That’s an interesting approach and I don’t see anything in the LRM to say you cannot do this.

The LRM places the following restrictions on variables:

Variables can only be driven by one continuous assignment or by one primitive output or module output. It shall be an error for a variable driven by a continuous assignment or output to have an initializer in the declaration or any procedural assignment [10.3.2].
It shall be an error to have multiple continuous assignments or a mixture of procedural and continuous assignments writing to any term in the expansion of a written longest static prefix of a variable [6.5]

I guess these are the checks that QuestaSim does not yet fully support. Since the virtual interface is only assigned in the generate statement, I don’t think this is anything you should worry about.

Unfortunately, this approach does not seem to be supported yet in Incisive.

Also verilog doesn’t allow to access an element of a generated block using a variable index like:
Quote:
usysif[0] = bfms[0].uwrap.lsysif;

When I played around with something similar to your example, I was initially assigning a generated interface to a virtual interface directly: inside a test class connect function. SystemVerilog allows an integer literal index (but not a variable) to access the generated interface here.

Regards,
Dave

Hi Dave,

Thanks for your inputs.:D

I hope so that this generate- assign pair will NOT have any problems in future releases of QuestaSim.

Thanks for trying it in Incisive:)

Did you referenced latest LRM? (recent drafts - 2008)

When I played around with something similar to your example, I was initially assigning a generated interface to a virtual interface directly: inside a test class connect function. SystemVerilog allows an integer literal index (but not a variable) to access the generated interface here.

Could you please elaborate on this approach??

Thanks in advance.

TVAR

Hi TVAR,

Did you referenced latest LRM? (recent drafts - 2008)

I was referencing the clauses in the Draft Standard (IEEE P1800/D3a) which you can purchase from the IEEE web store.

Could you please elaborate on this approach??

I was simply accessing the virtual interface in the driver with a hierarchical reference. e.g.

class test1 extends ovm_test;
  `ovm_component_utils(test1)

   verif_env env[`NUM];

   ...

   virtual function void build();
      super.build();
    for (int i=0; i < `NUM; i++)
      $cast(env[i],ovm_factory::create_component("verif_env","",$psprintf("env%0d",i),this) );
  endfunction : build


   function void connect();
        env[0].m_driver.if1 = test.c_gen[0].dut_if;
        env[1].m_driver.if1 = test.c_gen[1].dut_if;
        env[2].m_driver.if1 = test.c_gen[2].dut_if;
        env[3].m_driver.if1 = test.c_gen[3].dut_if;
       ...
   endfunction: connect
   

endclass: test1

I’m wondering if a virtual interface wrapper class and a suitable set_config_object expression could be used to give a tidier solution. I’ll let you know if I think of anything.

Regards,
Dave

Hi TVAR,

I’ve managed to get this working using a virtual interface wrapper class. I normally derive such a wrapper from ovm_object but I had to use an ovm_component base class here instead in order to get the top-level wrapper objects into the configuration database. I will go through the main points below.

Firstly, here is the virtual interface wrapper class. Note that the virtual interface reference is passed as a constructor argument.

class if_wrapper extends ovm_component;
   virtual chip_if if1;
   function new(string name,virtual chip_if if_,ovm_component parent = null);
      super.new(name,parent);
      if1 = if_; 
   endfunction : new
endclass : if_wrapper

I have used an instance of this wrapper in the driver and monitor classes.

class dut_driver extends ovm_driver    
   ovm_get_port #(basic_transaction) tx_in_port;
   virtual chip_if if1;
   if_wrapper if_wr;

  ...

   virtual function void connect();
      if1 = if_wr.if1;
   endfunction : connect

   task run();
      basic_transaction tx;   
      forever begin
         #10;    
         tx_in_port.get(tx);
         addr = tx.addr;
         data = tx.data;
         ovm_report_message("DRV",tx.convert2string());
         if1.driver.addr = addr;
         if1.driver.data = data;
      end
   endtask: run
   
    `ovm_component_utils_begin(dut_driver)
     `ovm_field_object(if_wr,OVM_ALL_ON)
   `ovm_component_utils_end
      
endclass: dut_driver

The monitor code is similar except it is connected to the DUT through a different modport. Note that the field automation macro for the interface wrapper is required to configure it.

In the top level module, I create a DUT, interface and wrapper object on each iteration of the generate loop.

module test;
    genvar i;

   generate
      for(i=0; i < `NUM; i++)
        begin: c_gen
           chip_if dut_if(.clk);
           chip dut ( .dut_if, .clk );
           if_wrapper if_wr  = new($psprintf("if_wrapper_%0d",i),dut_if);           
        end
   endgenerate


  ...

endmodule: test

The test class is also declared in the top level module. After creating each environment, it searches for the relevant wrapper using find_component which it then passes to set_config_object to fix the reference in the driver and monitor.

class test1 extends ovm_test;
  `ovm_component_utils(test1)

   verif_env env[`NUM];
   if_wrapper if_wr;

  ...

   virtual function void build();
      super.build();
      for (int i=0; i < `NUM; i++) begin
      $cast(env[i],ovm_factory::create_component("verif_env","",$psprintf("env%0d",i),this) );
      $cast(if_wr,find_component($psprintf("if_wrapper_%0d",i)));
      set_config_object($psprintf("env%0d.*",i),"if_wr",if_wr,0);
   end
  endfunction : build

endclass: test1

This compiles and runs with no errors or warnings on QuestaSim 6.3f and Incisive 6.20-s005.

Hope that gives you some ideas to solve your problems.

Regards,
Dave

Hi Dave,

Thanks for your detailed suggestion & approach.:)

It has really worked well in my environment.:D

Actually I have tried generating the if_wrapper derived from OVM object inside the generate statement, it doesn’t solve my issue.:(

using find_component & OVM component class is a very nice work around.:cool:

Thanks once again.

Regards,
TVAR

Hi Dave,

module test;
    genvar i;

   generate
      for(i=0; i < `NUM; i++)
        begin: c_gen
           chip_if dut_if(.clk);
           chip dut ( .dut_if, .clk );
           if_wrapper if_wr  = new($psprintf("if_wrapper_%0d",i),dut_if);           
        end
   endgenerate


  ...

endmodule: test

:cool: I think it’s a good bfm wrapper technique. But what if we need to have more than one environment and that too with all of them having different number of BFM instantiation. How can i control the # of instances at run-time (dynamically) and not at compile time using macros:confused:. Any suggestions.

Example:

In my system, I want to instantiate 2 AHB buses, each with different configuration i.e different # of masters, slaves and each with different bus widths

Thanks,
Saurabh Sharma

Anyone please post your suggestions:(

Hi Saurabh,

The other Dave here.

You are describing a common dilemma with IP developers; you need to test the reconfigurabilityof your code. Unfortunately, SystemVerilog and most other testbench languages can’t deal with dynamically sized vectors unless they are processed bit by bit, like a dynamic array or queue of bits.

I see at least three options for you:

  • Use a two step process where you first create a class or set of classes that randomize your configuration and writes out that configuration in the form of a SystemVerilog package or packages. Then use that package in the second step when compiling your complete design. You can also create a covergroup with one sample per simulation and merge the results over simulations to see which configurations have been covered.
  • If the number of customizations that deal with bit widths is manageable, i.e. two address bus widths and two data bus widths for a total of 4 configurations, you can create class specializations for each. Other customizations, like number of slaves, masters, and other modes can by dynamically randomized. Your DUT will also have to be class based with no interfaces. See [post]424[/post]
  • The brute force, and least preferable option is to treat all customizable bus widths as a dynamic array or queue. And until a simulator appears that supports operator overloading, you will have to manually manipulate the the bits to do anything other than a straight assignment.

Dave Rich

Hmm, a continuous assignment to a virtual interface in the generate block!!! That’s an interesting approach and I don’t see anything in the LRM to say you cannot do this.

As an interesting note the solution TVAR provided works fine if you switch the assign statement into an initial block… Basically…

module foo;
  virtual if_t ifs[2];
  genvar i;
  generate for (i = 0; i != 1-; i++) begin
        if_t my_if; 
       initial ifs[i] = my_if;
  end endgenerate;

  initial begin
     // Synchronize with the other initial blocks
     foreach(ifs[i]) wait(ifs[i] != null);

     env e = new;
     [..]
  end
endmodule

This works in Questa and probably others - you’d have to have a pretty broken simulator to not accept an assignment to a virtual interface. The foreach could even be replaced with #0;

Using assign on a virtual interface is probably bogus in general, and I’m not sure it even solves the race condition problem. (?)

Hi,

I would still recommend using a wrapper + set_config_object for virtual interfaces. Calling the global set_config_object can also be done from an initial block inside generate, e.g.

generate
      for(i=0; i < `NUM; i++)
        begin: c_gen            
           chip_if dut_if(.clk);
           chip dut ( .dut_if, .clk ); 
           if_wrapper if_wr  = new($psprintf("if_wrapper_%0d",i),dut_if);  
           initial set_config_object($psprintf("env%0d.*",i),"if_wr",if_wr,0);
        end
   endgenerate

Using the global configuration table removes the requirement to call find_component (or ovm_top.find in OVM 1.1) that I put in my original solution (it also allows the wrapper to be derived from ovm_object rather than ovm_component).

There are no issues with race conditions using this approach if the virtual interface is assigned during the driver’s connect method - OVM inserts a #0 between each phase so this always happens after the configuration has been set.

Regards,
Dave