Polymorphism in SystemVerilog

Polymorphism is one the most important OOP features used by the modern SystemVerilog test benches. This article explores a basic use case for this feature.

Assume a testbench needs to monitor a number of DUT interfaces. Those monitors need to be able to reset their internal queues and some state variables whenever reset event happens during the simulation. In addition to resetting queues and variables, they all have one common reset requirement. Here's the code that reflects those assumptions:

class mon_a;
...
function common();
...
endfunction : common
function mon_a_specific();
...
endfunction : mon_a_specific
function reset();
$write("Reset A in progress...\n");
common();
mon_a_specific();
...
endfunction : reset
endclass : mon_a

class mon_b;
...
function common();
...
endfunction : common
function mon_b_specific();
...
endfunction : mon_b_specific
function reset();
$write("Reset B in progress...\n");
common();
mon_b_specific();
...
endfunction : reset
endclass : mon_b

After instantiating the monitors in the test bench, we can create them and access their reset methods whenever reset conditions are met.

class tb_env;
...
mon_a m_mon_a;
mon_b m_mon_b;
...
mon_n m_mon_n;
...

m_mon_a = new();
m_mon_b = new();
...
m_mon_n = new();

... @ (reset) begin
   m_mon_a.reset();
   m_mon_b.reset();
   ...
end
...
endclass : tb_env

In the example above, each monitor has its own independent reset method. The solution is acceptable, but not efficient. We had to copy and paste the same code to make sure that mon_a and mon_b complete common() as part of their reset procedure. We also had to implement common() method in both classes and make sure it's exactly the same. Any subsequent change will result in excessive maintenance. Not only it's tedious and time consuming, it's also prone to errors. Moreover, any additional monitor will require to re-implement common(). Maintenance hardship increases as the number of monitors grows.

A solution to avoid the maintenance debacle is to create a base class and derive monitors and other dynamic components from it. Classes derived from mon_base can overwrite reset() method and by doing so capitalize on polymorphism. The reset() method in the base class takes care of the common functionality and the reset() methods in monitor classes deal with specifics.

class mon_base;
function common();
...
endfunction : common
virtual function reset();
$write("Reset in progress...\n");
common();
endfunction : reset
endclass : monbase

class mon_a extends mon_base;
...
function mon_a_specific();
...
endfunction : mon_a_specific
function reset();
super.reset();
mon_a_specific();
...
endfunction : reset
endclass : mon_a

class mon_b extends mon_base;
...
function mon_b_specific();
...
endfunction : mon_b_specific
function reset();
super.reset();
mon_a_specific();
...
endfunction : reset
endclass : mon_b

A keyword "virtual" must be used in declaration of reset() function in mon_base to make it available to be overwritten in derived class. Calling super.reset() in derived class enables functionality from the base class to be reused by the derivative. Those classes don't have to call super methods of course, and they can rewrite the base class methods from scratch.

Adding a base class saves time spent on maintenance and lowers a chance of accidental errors caused by copy and paste. In addition, tb_env becomes agnostic to the specific nature of each monitor when it reacts to the reset. Let's put all the monitors into one big array using the base class and see what happens:

class tb_env;
...
mon_base monitors[N];
...
mon_a Mon_a = new("Mon_a");
mon_b Mon_b = new("Mon_b");
...
monitors[0] = Mon_a;
monitors[1] = Mon_b;
...

@ (reset) begin
   foreach (monitors[i]) begin
      monitors[i].reset();
   end
end
...
endclass : tb_env 

From tb_env perspective, it deals with a number of identical components that have reset() methods. It doesn't care about specifics. Though tb_env sees all the objects as the objects of the base class type, whenever it calls reset(), it essentially calls reset() of the derived class, not the base class. 

In this trivial example, use of polymorphism allowed us to create a framework that can be reused by many testbenches in the future. This framework may be shared across multiple module level testbenches and subsystems in the same project and across multiple projects. In addition, it provides a common look and feel to all the testbenches and serves as a basis for a library.

Comments

Post a Comment

Popular posts from this blog

Interface class in SystemVerilog. What is it good for?