CFS Vision

Multiple Inheritance In SystemVerilog

I recently discovered that 2012 SystemVerilog standard introduced an extremely awesome feature called Interface Classes.

I’ll try to explain what it does as simple as possible:

In an interface class you can declare what functions you want a class to have.

A class can implement an interface class and when doing that it must provide an implementation for all the functions described in the interface class.

A class can implement as many interface classes as it wants.

If you don’t see yet how powerful this feature is don’t worry. In the example below I will show you how you can use this feature at its full potential.



Interface Class Usage Example

Let’s consider that we have an agent with an architecture as in the image below:

A Typical UVM Agent Architecture
A Typical UVM Agent Architecture

One common behavior that most of the components of an UVM agent must do is handle reset correctly.
Another feature more and more present in our life these days is low power. So some components inside our agent should handle this also.

Usually, the components aware of reset are: monitor, driver and sequencer.
For low power functionality let’s say that only the monitor and the driver must do something.

Low Power And Reset Features In an UVM Agent
Low Power And Reset Features In an UVM Agent

The Old (Bad) Way

A typical implementation for an agent with these requirements would be this one:
class cfs_monitor extends uvm_monitor;
...
virtual function void reset();
...
endfunction

virtual function void low_power_begin();
...
endfunction

virtual function void low_power_finish();
...
endfunction
endclass

class cfs_driver extends uvm_driver;
...
virtual function void do_reset();
...
endfunction

virtual function void lp_start();
...
endfunction

virtual function void lp_end();
...
endfunction
endclass

class cfs_sequencer extends uvm_sequencer;
...
virtual function void reset_started();
...
endfunction
endclass

The bad thing about this approach is that the agent code must know precisely what functions are implemented in what components:
class cfs_agent extends uvm_agent;
...
virtual function void handle_reset();
//agent must know what function for handling reset is implemented in each component
monitor.reset();
driver.do_reset();
sequencer.reset_started();
endfunction

virtual function void low_power_start();
//agent must know what function for handling low power start is implemented in each component
monitor.low_power_begin();
driver.lp_start();
endfunction

virtual function void low_power_end();
//agent must know what function for handling low power end is implemented in each component
monitor.low_power_finish();
driver.lp_end();
endfunction
endclass

This imply that if you realize that the coverage collector should also be aware of low power then you would have to add some functions in the coverage collector component and call them in the agent.
You can run this code on EDAPlayground.

The New (Good) Way

A better approach is to use interface classes to define a common API for the components which must be aware of reset and low power:
interface class cfs_reset_handler;

pure virtual function void handle_reset();

endclass

interface class cfs_low_power_handler;

pure virtual function void low_power_start();

pure virtual function void low_power_end();

endclass
Next you must implement the interface classes in the appropriate classes.
class cfs_monitor extends uvm_monitor implements cfs_reset_handler, cfs_low_power_handler;
...
virtual function void handle_reset();
...
endfunction

virtual function void low_power_start();
...
endfunction

virtual function void low_power_end();
...
endfunction
endclass

class cfs_driver extends uvm_driver implements cfs_reset_handler, cfs_low_power_handler;
...
virtual function void handle_reset();
...
endfunction

virtual function void low_power_start();
...
endfunction

virtual function void low_power_end();
...
endfunction
endclass

class cfs_sequencer extends uvm_sequencer implements cfs_reset_handler;
...
virtual function void handle_reset();
...
endfunction
endclass

Now comes the awesome part!!!
The agent is not directly aware of what component must handle reset or low power.
You just have to try to cast each child to a particular interface class:
class cfs_agent extends uvm_agent implements cfs_reset_handler, cfs_low_power_handler;
...
virtual function void handle_reset();
foreach(m_children[idx]) begin
cfs_reset_handler reset_handler;
if($cast(reset_handler, m_children[idx])) begin
reset_handler.handle_reset();
end
end
endfunction

virtual function void low_power_start();
foreach(m_children[idx]) begin
cfs_low_power_handler low_power_handler;
if($cast(low_power_handler, m_children[idx])) begin
low_power_handler.low_power_start();
end
end
endfunction

virtual function void low_power_end();
foreach(m_children[idx]) begin
cfs_low_power_handler low_power_handler;
if($cast(low_power_handler, m_children[idx])) begin
low_power_handler.low_power_end();
end
end
endfunction
endclass

In this way the agent implementation is more abstract and it will automatically take into consideration any new child which implements one of the two interface classes.

You can try out this code on EDAPlayground.

In all fairness I must say that this is not truly multiple inheritance because a class can not inherit members from an interface class or even implemented functions. At most I can say that via an interface class you inherit an API.

One last thing: I tried to run this code on all three major simulators but unfortunately only two worked (is anybody from San Jose reading this?)




If you want to gain an in-depth knowledge on how to do module level verification using SystemVerilog and UVM language then checkout my Udemy course called “Design Verification with SystemVerilog/UVM


Cristian Slav

9 comments