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:
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.
The Old (Bad) Way
A typical implementation for an agent with these requirements would be this one:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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?)
Always your post’s are something new.
Please add oftenly.
This approach is much preferable than multiple inheritance in C++/OOP. It let’s you choose the right interface class.
Great job with explaining the feature.
Very Useful information.Thanks for sharing
Thanks, good article! I’ve seen it in SV standard. It’s look like interface classes in Java.
Which simulators supports this implementation?
Hi
Great article. Just one question, what exactly is m_children[]?
m_children is a protected associative array declared inside uvm_component.
It holds references to all the components instantiated under it.
Hi ,
I am new to this m_children,you said it is declared inside uvm component dose it mean we can use it directly w/o declaring it in agent?
yes – you can use it directly