OVM wrapper for Verilog Bfms?

Hi All

Nice to have an active forum for OVM!!!

Just I have started looking into OVM & System Verilog.

It would be of great help, if anyone provides me with an approach/example/process steps on how to develop OVM wrapper for an existing verilog bfm like AVM?

Thanks in advance.

regards
TVAR

TVAR,

You can continue to use the process described in chapter 9 of the AVM cookbook. [update - this link in the UVM cookbook]

At the recent DVCON’08, I gave a paper that presents an alternative by using abstract classes. I have attached that paper and would like any feedback if you find it useful.

Dave

Attachment:

abstractBFM_final_DR6.pdf

Hi Dave,

Thanks for the DVCon’08 paper & details.

Non-availability of OVM Cookbook, has triggered me to post “OVM Wrapper for verilog Bfms??” question.

So far I have been using the legacy verilog BFM in AVM environment as per the Chapter 9 of AVM Cookbook.

I don’t want to change/modify the internals of the legacy verilog BFM such as the tasks & functions, internal transaction queues…etc.

Hence I have created a system verilog interface based module wrapper around the legacy verilog BFM.

Then I have created an Interface with a class consisting of virtual task calls through hierarchical reference to the internal tasks of the legacy verilog BFM.
This interface does not contain the BFM signals only their task calls and creates an instance of that class.

This interface is assigned to a virtual interface instance in the Driver/Stimulus generator class for driving the stimulus to the legacy verilog BFM.

This method (AVM class based wrapper ) works well & good, but still I have to use hierarchical referencing to access the internal tasks of the legacy verilog BFM.

Coming to the abstract base class method suggested in your paper, we are going to directly access the DUT signals instead of hierarchical referencing.

This seems to be good as far as the tasks in the concrete BFM class are NOT virtual and hierarchically calling internal tasks of the legacy verilog BFM.
This means that those tasks should be implemented in the current scope only.
This may add more code complexity & calls for change in the architecture of the legacy verilog BFM.

In my case, How can I directly call the internal tasks of the legacy BFM from the top module?

Please provide me your valuable suggestions on the same.

Thanks & Regards,
TVAR

In my case, How can I directly call the internal tasks of the legacy BFM from the top module?

I guess I’m having trouble understanding what is about the OVM that is preventing you from doing what you did in the AVM.

I’ll try to explain using the examples in the paper using OVM terminology. Let’s say this is your legacy BFM module:

module UART_Tx(output logic line);
  int NBits;
  time BitPeriod;
  task setNBits(input int N);
    if (N>0 && N<=10) NBits = N;
  endtask : setNBits
  task setBitPeriod(input time T);
    if (T>0) BitPeriod = T;
  endtask : setBitPeriod
  task send(input logic [9:0] d);
    ...
  endtask : send
endmodule : UART_Tx

And your legacy testbench looked like:

module top;
  wire line;
  UART_DUT  u_dut(line);
  UART_TX u_tx(line);
endmodule
module test;
  initial begin  
    top.U_tx.setNBits(8);
    top.U_tx.setNPeriod(100); 
      top.U_tx.send(8); 
      repeat (30) 
        top.U_tx.send($random);
    ...
endmodule

Now here’s the abstract class definition, defined as an ovm_object so it can be passed through a config object

package UART_pkg; 
  virtual class UART_BFM extends ovm_pkg::ovm_object;
    pure virtual task setNBits(input int N); 
    pure virtual task setBitPeriod(input time T); 
    pure virtual task send(input logic [9:0] d);
    function new(string name);
      super.new(name);
    endfunction
  endclass
endpackage : UART_pkg

Now in module test, or anywhere other than in a package, you define the concrete class, and register the config object:

module test;
  class U_TX extends UART_pkg::UART_BFM;
   
     task setNBits(input int N); 
        TB_top.U_tx.setNBits(N); 
     endtask 
     task setBitPeriod(input time T); 
       TB_top.U_tx.setNPeriod(100); 
     endtask 
     …
      function new(string name);
        super.new(name);
      endfunction
   endclass
  U_TX u_tx=new("U_TX");
initial set_config_object("driver_path","BFM",u_tx,0); // 0 - means don't clone
endmodule

Then in your driver (or monitor/whatever) you would use get_config_object to retrieve your handle:

import uart_pkg::*;
import ovm_pkg::*;
class uart_driver extends ovm_component;
  uart_bfm bfm_h;
  function void configure();
    ovm_object h;
    assert (!get_config_object("BFM",h) else ovm_report_error(...);
    $cast(bfm_h = h);
  endfunction
  task that_does_something(...);
  ...
  bfm_h.send(data);
    …
    endtask
endclass

Dave

Hi Dave,

Thanks for the detailed example.

I was able to work with Virtual Interface based approach in both AVM/OVM.

As the abstract base class method seems advantageous,according to your example, I have tried Abstract base class method.

I have placed the concrete base class & config object in the testbench module.

My simulation failed at $cast in the driver class with “BAD POINTER ACCESS” error. (Questasim 6.3d)

Error at the bolded line:

mport ovm_pkg::*;
class uart_driver extends ovm_component;
uart_bfm bfm_h;
function void configure();
ovm_object h;
assert (!get_config_object(“BFM”,h) else ovm_report_error(…);
$cast(bfm_h,h);
endfunction
task that_does_something(…);

bfm_h.send(data);
…
endtask
endclass
[/code]Dave[/QUOTE]

It is NOT able to cast the ovm_object ‘h’ to the object of the concrete base class ‘bfm_h’.

Please provide me your suggestoins.

Is h null before you do the $cast? I have to admit I didn’t try the example before posting, since it had a lot of …'s in it. (The example in the paper does work!). I’ll try to fill it out and get it working. I would help if you could attach the example as you typed it.

Dave

Hi Dave,

I do have a clarification with your example.

We can’t create an object of an abstract base class.

Hence how can I get an object of the abstract base class defined in the package?

Refer first bolded line.

import ovm_pkg::*;
class uart_driver extends ovm_component;
uart_bfm bfm_h;
function void configure();
ovm_object h;
assert (!get_config_object(“BFM”,h) else ovm_report_error(…);
$cast(bfm_h,h);
endfunction
task that_does_something(…);

bfm_h.send(data);
…
endtask
endclass
[/code]Dave

[/QUOTE]

Hi Dave,
I do have a clarification with your example.
We can’t create an object of an abstract base class.
Hence how can I get an object of the abstract base class defined in the package?

That is correct, you can’t construct an object of an abstract base class. But you can extend an abstract class and construct that class, and assign that object to a handle of an abstract class.

Hi Anantv, Dave_59,

There is another method being discussed for module based bfms (not AVM) that may be useful for Anant to look at. [HTML]http://www.ovmworld.org/forums/showthread.php?t=100
[/HTML]

Umer

Hi Dave,

Thanks for the detailed example, which helped me to understand the approach clearly.

Hi Umery,

Thanks for referring to the thread:
http://ovmworld.org/forums/showthread.php?t=109

This gives another approach for the same.

Thanks to all.

Hello,

I have been looking through the methodologies. How do I handle Multiple Instance of
UART.

UART_TX U_tx0(line); UART_TX U_tx1(line);

Now Since their instance path have to hard coded in the concrete bfm class, one might
need that many class definitions…

class U_tx_c0 extends uart_pkg::uart_api_c;
class U_tx_c1 extends uart_pkg::uart_api_c;

In this example the number of tasks in the BFM is small. I am using BFMs that have
a large number of tasks. Whats the solution ?

(
It would be nice if Verilog permitted array like module instance… then I can pass the
index of the instance to the constructor of the concrete bfm class.

UART_TX U_tx0; UART_TX U_tx1;
)

Whats the proposed solution ?

Thanks,
Kiran

I’ve updated the example to use some more recently developed techniques. This example uses bindand the factory to create the objects

Dave

Thanks for sending out this example…

Never come across the BIND construct so far…

bind UART_TX : top.BLOCK[i].U_tx UART_TX_probe p();

Whats the Implication of “UART_TX :” following the bind command ? I could not find this
syntax in the LRM.

Also, when put in a for look, the module UART_TX_probe gets instance name p() ? Can you explain the mechanism behind this ? What does the run time environment do ? Make N
instances of UART_TX_probe ?

Thanks… I did some experiments and figured… the answers:

so its going to be top.BLOCK[i].U_tx.p

Also whats the standard for the

bind : instance const bind_inst ??
OR
bind inst const bind_inst

LRM says:
bind_directive ::= bind hierarchical_identifier constant_select bind_instantiation ;
bind_instantiation ::=
program_instantiation
| module_instantiation
| interface_instantiation

You must be looking at an old LRM. 1800-2009 says

bind_directive ::= 
     bind bind_target_scope [: bind_target_instance_list] bind_instantiation ; 
     | bind bind_target_instance bind_instantiation ;

You have to use the first option if you have a list of instances you want to bind to. Providing the bind_target_scope is also a good sanity check to make sure you are binding into the correct instance.

Dave

Thanks Dave…

Also, I am wondering… whats the value add in redefining all the tasks in the
abstract and concrete classes… It serves its purpose in making it a ovm_object and
provides an handle to the driver… so the driver can call the tasks as it see fit…

Whats your opinion ?

Hi, I am not clear about the code in abstract.sv.
I find a compile error under questa6.5b, such as following:
In non-vopt mode, only literal expressions are supported as indices in a bind target hierarchical reference. Please run vlog in the vopt flow.

Much thanks~

I moved the bind_target module to the same level as the module in which i was binding and this error went away…

I am not sure what it means, but thats the outcome.

Thanks Dave…
Also, I am wondering… whats the value add in redefining all the tasks in the
abstract and concrete classes… It serves its purpose in making it a ovm_object and
provides an handle to the driver… so the driver can call the tasks as it see fit…

The value is the driver can call the tasks with out a hierarchical reference to the DUT, and without any dependency on the DUT. In fact, the concrete classes could be replaced by other non-RTL model, removing the DUT, without the driver ever knowing it.

BTW, the issues with bind and no-vopt is a tool issue that should be discussed directly with Mentor.

Dave

I guess I’m having trouble understanding what is about the OVM that is preventing you from doing what you did in the AVM.
I’ll try to explain using the examples in the paper using OVM terminology. Let’s say this is your legacy BFM module:

module UART_Tx(output logic line);
int NBits;
time BitPeriod;
task setNBits(input int N);
if (N>0 && N<=10) NBits = N;
endtask : setNBits
task setBitPeriod(input time T);
if (T>0) BitPeriod = T;
endtask : setBitPeriod
task send(input logic [9:0] d);
...
endtask : send
endmodule : UART_Tx

And your legacy testbench looked like:

module top;
wire line;
UART_DUT  u_dut(line);
UART_TX u_tx(line);
endmodule
module test;
initial begin  
top.U_tx.setNBits(8);
top.U_tx.setNPeriod(100); 
top.U_tx.send(8); 
repeat (30) 
top.U_tx.send($random);
...
endmodule

Now here’s the abstract class definition, defined as an ovm_object so it can be passed through a config object

package UART_pkg; 
virtual class UART_BFM extends ovm_pkg::ovm_object;
pure virtual task setNBits(input int N); 
pure virtual task setBitPeriod(input time T); 
pure virtual task send(input logic [9:0] d);
function new(string name);
super.new(name);
endfunction
endclass
endpackage : UART_pkg

Now in module test, or anywhere other than in a package, you define the concrete class, and register the config object:

module test;
class U_TX extends UART_pkg::UART_BFM;
task setNBits(input int N); 
TB_top.U_tx.setNBits(N); 
endtask 
task setBitPeriod(input time T); 
TB_top.U_tx.setNPeriod(100); 
endtask 
…
function new(string name);
super.new(name);
endfunction
endclass
U_TX u_tx=new("U_TX");
initial set_config_object("driver_path","BFM",u_tx,0); // 0 - means don't clone
endmodule

Then in your driver (or monitor/whatever) you would use get_config_object to retrieve your handle:

import uart_pkg::*;
import ovm_pkg::*;
class uart_driver extends ovm_component;
uart_bfm bfm_h;
function void configure();
ovm_object h;
assert (!get_config_object("BFM",h) else ovm_report_error(...);
$cast(bfm_h = h);
endfunction
task that_does_something(...);
...
bfm_h.send(data);
…
endtask
endclass

Dave

===============
Those coming from C++ background may recognize the elegant design solution presented here is an exercise in the application of what’s known as an Adapter Design Pattern in OOP. A solution for a problem already exist (as Verilog code), question is: how to use it without duplicating it? That is, how to interface it with OVM driver class? Four-step solution: (1) create abstract class (2) Derive concrete class from abstract class (3) Derived class has to have some way of knowing (i.e. reference) to legacy code, and finally (4) Driver class has to have a reference to abstract class.