Verification Martial Arts: A Verification Methodology Blog

The hidden pitfalls of type name hiding in a derived class

Posted by Wei-Hua Han on May 9th, 2009

Weihua Han, CAE, Synopsys

Here I describe one major difference between virtual and non-virtual methods, and how type name hiding can collide with these methods. This is commonly used OOP feature in SystemVerilog.

“type name hiding” here refers to the situation where a user type definition in the derived class uses the same type name defined in base class, i.e, the new type definition in derived class “hides” the type definition in base class.

SystemVerilog LRM (Language Reference Manual) does not forbid hiding base class type definition in a derived class.  Here’s a typical example:

  1. class company_xactor_c;
  2.         typedef enum { SOFT, HARD } reset_e;
  3.         function void do_reset (reset_e rst);
  4.                 if(rst==SOFT)
  5.                         $display("Do SOFT reset");
  6.                 else
  7.                         $display("Do HARD reset");
  8.         endfunction
  9. endclass
  10. class pci_prj_xactor_c extend company_xactor_c;
  11.                 typedef enum { SOFT, HARD, PCI } reset_e; 
  12.                 function void do_reset(reset_e rst);
  13.                         if(rst==SOFT)..
  14.                         else if(rst==HARD)…
  15.                         else ….
  16.                 endfunction
  17. endclass

Here we intend to have a new “reset_e” definition in PCI project by adding a new PCI specific label called “PCI”.  Next, we also rewrite the “do_reset” method which now comes with a specific “reset” behavior for PCI project.  Then, all transactors derived from “pci_prj_xactor_c” for this PCI project can use this “PCI” label to define PCI specific behaviors.

It seems everything works fine until now. But we will see problems crop up when virtual function (polymorphism) comes into the picture. 

Below is a simplified but realistic scenario.

As you know, VMM allows you to extend the base class library. It’s not uncommon for you to add some specific methods and to create your own base class library, such as:

  1. class company_xactor extends vmm_xactor;
  2.         virtual function void tb_start(int nth_run,
  3. reset_e  reset_e_list[$]);
  4.         …
  5.         endfunction
  6. endclass

Here a company-wide “tb_start” method is added to the company-wide transactor class. And “reset_e” here is derived from vmm_xactor. We may also have a project specific transactor base class like:

  1. class project_xactor extends company_xactor; 
  2.                 typedef enum int { 
  3.                          SOFT_RST, 
  4.                          PROTOCOL_RST, 
  5.                          FIRM_RST, 
  6.                          HARD_RST, 
  7.                          PON, 
  8.                          PCIE 
  9.                 } reset_e; 
  10.     virtual function void tb_start(int nth_run,  reset_e  reset_e_list[$]); 
  11.             … 
  12.             endfunction
  13. endclass

And we have a new “reset_e” definition with some new project specific labels.  “tb_start” is also refined for the project.

Although this may seem fine, there is a fairly serious error in this code: the virtual function in SystemVerilog class follows similar common OOP polymorphism requirement.

These OOP requirements are described in SystemVerilog LRM. Here is an excerpt:

“Virtual methods provide prototypes for the methods that later override them, i.e., all of the information generally found on the first line of a method declaration: the encapsulation criteria, the type and number of arguments, and the return type if it is needed. Later, when subclasses override virtual methods, they shall follow the prototype exactly by having matching return types and matching argument names, types, and directions. It is not necessary to have matching default expressions, but the presence of a default shall match. ”

In above code, “reset_e” definition in derived project_xactor class actually defines a new type, i.e, project_xactor::reset_e, which is a different one as the “reset_e” defined in vmm_xactor.  Now the prototypes of tb_start in company_xactor is
      void company_xactor::tb_reset(int nth_run, vmm_xactor::reset_e reset_e_list[$]).
And in project_xactor it is:
      void project_xactor::tb_reset(int nth_run, project_xactor::reset_e reset_e_list[$]).
These are different.  They are not in compliance with the LRM. Sure, a user can omit to make “tb_start” virtual.  Technically, this will work fine.  However, in this case, all the benefits of using polymorphism will be lost.

Although type name hiding is allowed, we need to be careful in how it is used, since polymorphism is quite commonly employed in VMM and in most testbench environments. This is a very important aspect of reuse and extendibility.

In my next blog post, I will discuss more about what is overriding, what is hiding, what are overridden and what are hiden in SystemVerilog.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Google Buzz
  • LinkedIn
  • RSS
  • Twitter

One Response to “The hidden pitfalls of type name hiding in a derived class”

  1. Nick Castiglia Says:

    Hi Weihua,

    I really enjoyed who you differentiated between virtual and non virtual methods. The part about the reset was really helpful.

    Thanks again,

    Nick

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>