In a previous post, I said that in SystemVerilog, once you specialize a class, you can not make a group of them. Oops! Turns out that UVM does this all this time. You just need to know where to start.
Just to be clear, you are making a group of handles, an array. Every object is separate, and thus cannot organized into an array.
Where’s my sequencer?
Here is an example of a group of specialized classes. Your design may speak several protocols, so your UVM testbench needs multiple agents. In UVM, an agent speaks one protocol, such as USB or AXI. Inside the agent, a sequencer picks incoming transactions and sends them to the driver. The uvm_sequencer class is specialized for a single flavor of transactions, like these declarations.
// Inside the AXI agent class
uvm_sequencer #(axi_txn) axi_sqr; // A sequencer for AXI transactions
// Inside the USB agent class
uvm_sequencer #(usb_txn) usb_sqr; // A sequencer for USB transactions
How can your testbench organize all those sequencers? What if your testbench declared an array of handles to hold these? Is the following code legal?
sqr_array["AXI"] = axi_sqr; // Compile error
sqr_array["USB"] = usb_sqr; // Compile error
This won’t compile because when you specialized the class uvm_sequencer, it created a new type, and this is not compatible with the base uvm_sequencer, or the USB specialization. The full example requires a lot of support code, so let’s make a smaller example, without UVM.
Back to basics
There is a solution, which you can see by peeking at the UVM source code where the parameterized class uvm_sequencer is eventually extended from the non-parameterized class, uvm_sequencer base.
Let’s make a simple base class with a name property, and no parameters. The function print() displays the class name.
virtual function void print();
Now make a derived class that has a integer parameter.
class Dparm #(int N=42) extends Base;
bit [N-1:0] p;
function void print();
Now you can make a group of handles. Declare them as an associative array of the base type, called a pool. Also declare another base handle, and a few of the derived type with different specializations.
Base bpool[string], b; // Pool of base handles, and temp
Dparm#(1) d1; // Derived with #1
Dparm#(2) d2; // Derived with #2
Does this work? Can you really make an array of handles that point to different specializations? Try this on your favorite simulator and see what happens.
b = new("b"); // Base handle to base object
bpool["b"] = b; // Save in base pool
d1 = new("d1"); //
bpool["d1"] = d1; // Save in base pool
d2 = new("d2"); //
bpool["d2"] = d2; // Save in base pool
$display("Print the object types");
foreach (pool[i]) // Wade through pool
pool[i].print(); // and print objects
Passing and assigning handles
You can assign a handle of a specialized type to a base handle. Here is a module-level function that calls the print() method.
function void print(input Base b_arg);
You can call it as follows.
print(b); // Print base object
print(d1); // Print some specialized types
print(d2); // Equivalent to: b_arg = d2;
What if you want to assign to a derived specialized handle from a base handle? I’m out of space, so you are going to have to peek at a previous SystemVerilog OOP post and see what works.
When you specialize a parameterized class, you are creating a new type, incompatible with other specializations. However, if the parameterized class extends a base class, you can assign a specialized handle to a base handle.
You can learn more about these topics including Oriented Programming with the Siemens SystemVerilog for Verification course. It is offered in instructor led format by our industry expert instructors, or in a self-paced on-demand format. It can also be tailored to address your specific design goals and show you how to set up an environment for reuse for additional designs. Also, you can now earn a digital badge/level 1 certificate by taking our Advanced Topics Badging Exam. This will enable you to showcase your knowledge of this topic by displaying the badge in your social media and email signature.