[sv-ec] Multiple Inheritance - more details and examples

From: Alsop, Thomas R <thomas.r.alsop@intel.com>
Date: Mon Jun 21 2010 - 10:55:54 PDT

Hi Everyone,

Just want to follow up with the multiple inheritance mantis item. I have a follow up email from one of our verification experts that explains in more detail the concepts that he wants to enable in SVTB code. First here is the original mantis material on the subject. Down below it you will see the additional details and examples. Hopefully this will jump start the discussion today.

 * SV-EC - Multiple Inheritance - Open issue ID 0001356 regarding multiple inheritance. Intel would like to see multiple inheritances implemented, specifically with Java-like restrictions. i.e. allowing multiple inheritance extending zero or more pure virtual classes and up to one other class. (In Java, a special type of pure virtual class is called an "interface" instead of a class). C++ is a little too relaxed on allowing multiple inheritance of non-pure-virtual classes which can get you into trouble.
Java Style interfaces (basically a completely virtual class that other classes must "implement") (http://java.sun.com/docs/books/tutorial/java/concepts/interface.html) These are VERY different from pure virtual classes. It is a mechanism for achieving the goodness of multiple inheritance, without the complexity of multiple inheritance. The key is the "implements" keyword. A single class can "implement" multiple "interfaces". This is different from a pure virtual class where the implementing class can only "extend" a single pure virtual class. This feature is used heavily in Java, and is an important feature for code maintainability. (Contributor Paul Baleme)

talsop - From my reading of the Java style interfaces webpage, an interface, as defined there, describes a set of methods that have a specific function in life. It's a way of spec'ing out the behavior of a method. The way the 'interface' is used is to promise to 'implement' them in a class by declaring the class like this

        class ACMEBicycle implements Bicycle {

This enforces the compiler to implement the method defined in the 'interface' Bicycle. It's basically the class promising to implement those methods. If it doesn't the class will not compile.

interface Bicycle {

       void changeCadence(int newValue); // wheel revolutions per minute

       void changeGear(int newValue);

       void speedUp(int increment);

       void applyBrakes(int decrement);

}

More details:

In Java, the key difference is the ability to implement one or more interfaces while simultaneously extending one class. For me, it's the multiple part of "multiple inheritance" that's important. Java-style interfaces add some restrictions to help keep you out of trouble that you could get into with a C++ style multiple inheritance free-for-all. For a common example of where this could be used, take a look at the ovm_*_imp classes in OVM. The ovm_*_imp object must be declared as a member of the class which is parameterized with the parent class' type, and it must be instantiated before you can connect it to a port. The OVM SV library itself is confusing what should be an IS-A relationship with HAS-A simply because multiple inheritance isn't a feature of the language.

For example, you should be able to just write this:

class my_driver extends ovm_driver implements ovm_put_imp #(my_cycle);
  task put(my_cycle cycle);
    ...
  endtask : put
endclass : my_driver

or, with C++ style multiple inheritance, you would write something like this:

class my_driver extends ovm_driver, ovm_put_imp #(my_cycle);
  task put(my_cycle cycle);
    ...
  endtask : put
endclass : my_driver

instead, you have to write this nonsense:

class my_driver extends ovm_driver;
  // this makes the fact that the driver implements a put interface a HAS-A relationship, when it really
  // should be an IS-A (or simply implements) relationship. The driver implements the put task, it should
  // not have to contain a member to advertise that it implements that functionality.
  ovm_put_imp #(my_cycle, my_driver) my_put_imp; // Note: the containing class must be passed as a parameter

  function build();
    // since the imp is an object, it must be instantiated
    ovm_put_imp = new("my_put_imp");
  endfunction : build

  task put(my_cycle, cycle);
    ...
  endtask : put
endclass : my_driver

What I've written above doesn't even begin to go into all of the nonsense code that goes into the ovm library ovm_put_imp class to be able to implement the functionality that could be simply covered by multiple inheritance (even with Java-style limitations). With Java-style interfaces (multiple inheritance), the ovm_put_imp class would just need to have a pure virtual task "put" and nothing else. Here is the actual ovm_put_class from the OVM library:

00097<http://www.intelligentdv.com/documents/doxygen/ovm-2.1.0/classovm__put__imp.html> class ovm_put_imp<http://www.intelligentdv.com/documents/doxygen/ovm-2.1.0/classovm__put__imp.html> #(type T=int, type IMP=int)
00098 extends ovm_port_base<http://www.intelligentdv.com/documents/doxygen/ovm-2.1.0/classovm__port__base.html> #(tlm_if_base<http://www.intelligentdv.com/documents/doxygen/ovm-2.1.0/classtlm__if__base.html> #(T,T));
00099<http://www.intelligentdv.com/documents/doxygen/ovm-2.1.0/classovm__put__imp.html#aa8fbb9cb087634c06d65a2a34838dd2b> `OVM_IMP_COMMON<http://www.intelligentdv.com/documents/doxygen/ovm-2.1.0/tlm__imps_8svh.html#a93169770a0cf94bbd93bbc4ed809b0c5>(`TLM_PUT_MASK<http://www.intelligentdv.com/documents/doxygen/ovm-2.1.0/tlm__defines_8svh.html#acd92fd61d6342b420fae13f8db9f93f3>,"ovm_put_imp",IMP)
00100<http://www.intelligentdv.com/documents/doxygen/ovm-2.1.0/classovm__put__imp.html#ac2b5ed21eac6d31d59947174737eb9a3> `PUT_IMP<http://www.intelligentdv.com/documents/doxygen/ovm-2.1.0/tlm__imps_8svh.html#aa3cd16ac369041fc7dc836c0ee35a97a> (m_imp<http://www.intelligentdv.com/documents/doxygen/ovm-2.1.0/classovm__blocking__put__imp.html#a61c58383aea18685c53ec12af3d83a09>, T, t)
00101 endclass

The above looks relatively simple, but the base class, ovm_port_base, is over 750 lines long. The macros above contain another chain of macros, but they would expand to about 10-20 lines total. Admittedly, it isn't an apples-to-apples comparison; since the ovm_put_imp had to be its own class, a bunch of (unnecessary) functionality was added for good measure. But if you go through the code and look at what's required, I'd estimate that there's a good 200-300 lines of convoluted code that would be eliminated if multiple inheritance was allowed by the language.

As a bonus example, here is another annoying example that I have been faced with:

In my current test environment, I have BFMs (VCs) that are coming from multiple different sources. I would like to present to test writers a consistent interface for all BFMs, but since each BFM comes from a different place, they all come with different APIs. What I would do, for each BFM, is extend the class that is given to me and then implement my consistent API on top of that. E.g.:

class bus1BFM extends vendor1_bus1_bfm implements my_env_bfm_interface;
  virtual function my_consistent_function();
    vendor1_specific_function_call();
    ....
endclass : bus1BFM

class bus2BFM extends vendor2_bus1_bfm implements my_env_bfm_interface;
  virtual function my_consistent_function();
    vendor2_specific_function_call();
    ...
endclass : bus2BFM

What is interesting about the above, is that polymorphism should work on an object of "my_env_bfm_interface" type. i.e., I can then do the following in the testbench:

my_env_bfm_interface bfm1;
my_env_bfm_interface bfm2;

bfm1 = bus1BFM::new(); // this isn't actually supported in SV right now, but you get the idea
bfm2 = bus2BFM::new(); // ditto

bus1.my_consistent_function();
bus2.my_consistent_function();

Without multiple inheritance, I could (and am) providing a piece of glue logic which does the same translation, but that work-around has various disadvantages compare the using an interface.

-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
Received on Mon Jun 21 10:56:23 2010

This archive was generated by hypermail 2.1.8 : Mon Jun 21 2010 - 10:56:28 PDT