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.