Interface class in SystemVerilog. What is it good for?

SystemVerilog is an object oriented language. Unfortunately, not all the features inherent in such a language are available. Multiple inheritance is one of them. The new SystemVerilog standard IEEE Std 1800-2012 (SV12) introduces a new class type called "interface class" which can be extended from two or more other interface classes. Unfortunately, the multiple inheritance starts and ends with this type of the class, and due to strict limitations of what can be done with it, the new class type won't solve a problem described below. Moreover, we can't really talk about inheritance when we talk about Interface class.

With continuous migration to standard verification methodologies, such as VMM, OVM, and UVM, the lack of ability to inherit from multiple classes causes a lot of grief to the testbench developers. Every SystemVerilog based verification methodology offers a library full of methodology specific base classes. It's essential to extend from those classes when building business unit libraries which are later used as a foundation for  testbenches used to verify units, subsystems, IP's, or systems on chip. Lately, UVM (Universal verification methodology) emerges as the most popular in the industry. I will use it to demonstrate a problem which may require multiple inheritance.

Some of the core building blocks of UVM testbench are drivers and monitors. UVM provides base classes for both of them. Those classes are extensions of uvm_component. When building a business unit library it's best to create classes in such a way that a testbench developer would never need to extend from a methodology class. It helps to make the testbench methodology agnostic and adds a layer which enables bringing some business unit specific functionality without modifying existing testbenches.

In case of UVM, let's assume there is a need for business unit extensions of all dynamic objects in the testbench to have some common method which may be called during one of the UVM phases. Since every dynamic object is an extension of uvm_component, it's a good idea to create bu_component as an extension of uvm_component, and place the required methods there. This approach dictates the rest of the library components to extend from bu_component. There is a problem though, since uvm_driver and uvm_monitor are left out, becasue they do not extend from bu_component. If you look at those classes in UVM 1.1, they don't seem like much, but cutting them away will prevent from benefiting from future methodology developments. 

An ideal solution to this problem would be the use multiple inheritance, which means, the bu_driver should inherit both, bu_component and uvm_driver. But how to do that, if SystemVerilog doesn't allow extension from multiple classes? There are multiple ways to achieve the desired.

One way is to copy contents of uvm_driver and uvm_monitor into bu_driver and bu_monitor respectively, while bu_driver and bu_monitor extend from bu_component. It can be done using scripts each time a new UVM version is introduced. Another way is to create a wrapper around uvm_driver or uvm_monitor instance, which extends from bu_component. This way there is no need to update the business unit classes when a new version of UVM is published, but the wrapper adds additional layer of complexity.

Even though we can extend an Interface class from two or more other Interface classes, it doesn't mean there is multiple inheritance. The reason is - there is nothing to inherit. Interface class defines parameters, type definitions and methods. It cannot provide implementations or variables. In fact, 1800-2012 specifically mentions that no part of Interface class is ever inherited when some other class implements its methods. Even parameters defined in Interface class cannot be referred directly, they are only accessible through the scope resolution operator. Whenever a class extends a parent and implements Interface class, there is no multiple inheritance. Moreover, the class cannot implement two versions of the method defined Interface class. Parametrizing won't help, since SystemVerilog does not support two methods with the same name but different output or variables type. In other words, there is no C++ style polymorphism in SystemVerilog.

Interface classes can be used to provide an elegant solution to list methods required by business unit specific functionality, making sure any business unit extension of methodology class adheres to the business unit rules. They help to make code more modular and self explanatory. They also allow to standardize methods names which perform similar functions. If needed, no method declaration can be allowed inside implementation classes, creating a clear divide between declarations and implementations. Functionally, there is nothing new this class is bringing to the table. It provides some other way of doing the same things, but doesn't make your life easier or tougher. It can help enforce some coding and naming conventions, and that's about it.

The example below demonstrates the use of interface class: 

interface class bu_interface_class #(type T = bit);
   typedef enum {
                 EN_PASS,
                 EN_FAIL,
                 EN_INCONCLUSIVE
                 } t_buStatus;
   
   pure virtual function string BuSpecificMethod(T Var);
endclass : bu_interface_class

class vm_base_class;

   string name;
   
   rand bit a;
   rand int b;

   function new(string name = "vm_base");
      this.name = name;
   endfunction : new
   
   virtual function string psdisplay(string prefix);
      psdisplay = $psprintf("%s:%s: a=%0b, b=%0d",prefix,name,a,b);
   endfunction : psdisplay
         
endclass : vm_base_class

virtual class bu_base_class extends vm_base_class implements bu_interface_class #(bit);
   rand bu_interface_class#(bit)::t_buStatus Status;

   function new(string name = "test");
      super.new(name);
   endfunction : new
   
   virtual function string BuSpecificMethod(bit Var);
      if (Var == 1)
        BuSpecificMethod = Status.name();
      else
        BuSpecificMethod = "Unsupported";
   endfunction : BuSpecificMethod

   virtual function string psdisplay(string prefix);
      psdisplay = $psprintf("%s %s",
                  super.psdisplay(prefix),
                  BuSpecificMethod(1'b1));
   endfunction : psdisplay
   
endclass : bu_base_class


class test_class extends bu_base_class;

   constraint c_test {
      a == 0;
      b inside {[1:10]};
      Status inside {bu_interface_class#(bit)::EN_PASS,bu_interface_class#(bit)::EN_FAIL};
   }

   function new(string name = "test");
      super.new(name);
   endfunction : new
   
endclass : test_class

   
program test();
   vm_base_class Vm;
   test_class    TestVm;
   
   initial begin
      Vm     = new("base_vm");
      TestVm = new("test_vm");
      
      TestVm.randomize();
      Vm.randomize();

      $display(Vm.psdisplay("TEST"));
      $display(TestVm.psdisplay("TEST"));
      $finish;
   end
   
endprogram : test

The code above produces the following simulation results:

TEST:base_vm: a=0, b=-2124444300
TEST:test_vm: a=0, b=8 EN_PASS
$finish called from file "ex01.sv", line 80.
$finish at simulation time                    0


As you can see in this example, I was able to extend from a methodology specific base class, while adding a business specific method at the same time. I was forced to implement the method defined by interface class to adhere to the business rules. In case the business unit specific functionality requires a change (add methods or change a type or a number of variables), once interface class has changed, my extension most likely won't compile, which will prompt me to review my implementation and fix the code accordingly. 

The same result could have been achieved without using Interface class. I don't find enough compelling reasons to start using it on a mature testbench. It can be considered while developing new libraries and to enforce naming conventions across multiple testbenches.

Comments

Popular posts from this blog

Polymorphism in SystemVerilog