Sunday, December 6, 2015

Packages, Class Names and UVM

Some time ago I wrote a post that challenged some of the established coding conventions of modern SystemVerilog. In particular, I expressed my displeasure with the fact that all training material from EDA companies, tutorial sites and other learning resources state that packages should always contain a "_pkg" suffix appended to the package name and that all identifiers in the package (class/function/constant names) should contain the package name as a prefix. I attribute this to the significant C legacy that exists in our field, as the C language doesn't have any construct for packaging code.

I've started to drop the package name prefix from any new code I'm writing, both for the blog (as you might have noticed), but also at work. By seeing how this works out in "real life", I've noticed some pitfalls. The first is, of course, that people will come and complain that this doesn't satisfy the commandments given to us by the lords of SystemVerilog. I've yet to hear any compelling argument against dropping package names from classes. Moreover, the only arguments I've ever heard were "this isn't how everybody else is doing it" and my favorite "we've always done it this way". Until someone can come up with something better, I'll continue to believe that the much larger communities of C++, Java and other modern programming languages are onto something.

Now let's look at what happens when applying this idea when also using UVM. Normally, we'd have a package that contains a class definition. Inside this class, we'd use the utils macro to reduce the amount of boilerplate code needed to make it a productive member of a UVM environment:

package some_package;
  // ...

  class some_class extends uvm_object;
    // ...

    `uvm_object_utils(some_class)
  endclass

endpackage

If we'd try to print an object of this class, we'd get something like this:

---------------------------------
Name      Type        Size  Value
---------------------------------
some_obj  some_class  -     @338
---------------------------------

The type column would rightly show some_class, but that isn't very informative, as some colleague pointed out. Having the package name as a prefix made it instantly possible to identify the scope where the class is defined. This is particularly helpful when classes from different packages use the same name.

And speaking of using the same name for multiple classes... Let's say that we also have another package that defines a some_class type:

package some_other_package;
  // ...

  class some_class extends uvm_object;
    // ...

    `uvm_object_utils(some_class)
  endclass

endpackage

Because the classes have the same name, when they get registered with the factory, we'll get the following warning:

UVM_WARNING @ 0: reporter [TPRGED] Type name 'some_class' already registered with factory. No 
string-based lookup support for multiple types with the same type name.

Aside from disabling the set_*_override_by_name(...) functions (which I anyway wouldn't recommend using), it doesn't do anything else. Everything else still works just fine. Nevertheless, extra warning message aren't nice, because they clutter the log file. For one or two classes it might be ok, but try working with multiple UVC packages that each define a driver, monitor, agent, etc. class... I tried to come up with a way to disable the warning, but I wasn't successful.

I've thought about these problems on multiple occasions, went down a few dead ends and dreamt up some silly solutions. I kept thinking that the problem was with UVM, that the macros were to restrictive because they don't consider the class's parent package. Then I realized that the name that gets displayed by the print(...) function and that gets registered with the factory is merely the one supplied as the macro argument. Instead of using just the class name, we can just as well use its fully qualified name, that includes the package name and the scope operator, "::". This means we can change our code to this:

  class some_class extends uvm_object;
    // ...

    `uvm_object_utils(some_package::some_class)
  endclass

Now we won't get any more warning from the UVM factory and the text displayed by print(...) will make it clear which class we're dealing with:

-----------------------------------------------
Name      Type                      Size  Value
-----------------------------------------------
some_obj  some_package::some_class  -     @338
-----------------------------------------------

With this small tweak, it's possible to drop the package prefix from classes while still getting nice prints in UVM and avoiding any warnings from the factory. Now we have two reasons less against shortening our class names.

16 comments:

  1. Awesome! Keep fighting the good fight here :-)

    ReplyDelete
  2. "all identifiers in the package (class/function/constant names) should contain the package name as a suffix"

    Did you mean to say prefix instead of suffix? That's the pattern that I observed to be mostly used.

    ReplyDelete
    Replies
    1. I did mean to write prefix. It was a typo that I fixed now.

      Delete
  3. Our company recommendations are that packages have the suffix _pkg, classes have the suffix _c, and a variety of other suffixes. By keeping the suffixes short, you have less to type (and hence, fewer bugs). So, your example above would be:
    `uvm_object_utils(some_pkg::some_c)

    Instances within the package don't need the package scope since it's implicit. So you would declare them as follows:

    some_c some; // maybe 'some' isn't a good example...
    drv_c drv; // here is a driver, again following our conventions

    And instances outside of the package are required to use the scope resolution operator:

    some_pkg::drv_c some_drv;

    Forgive the blatant plug here: All of the UVM coding guidelines that we have successfully followed for 4 years now are given in my book, Advanced UVM (Amazon).

    ReplyDelete
    Replies
    1. "classes have the suffix _c"
      What do you use for constraints?
      Does your firm publish their coding guidelines?

      Delete
  4. Sigh... then someone will just include your newly created class in two different packages. :(

    ReplyDelete
    Replies
    1. I don't fully understand this downside you are pointing out, hevangel. Would you mind describing this scenario more for me?

      Delete
  5. using the fully qualified name to avoid the TPRGED warnings is especially useful when you are using the UVM regmodel very hierarchically, and allowing the same register class names at different levels of hierarchy.

    ReplyDelete
  6. The benefits of this style makes sense to me. It's been quite a few years now, Tudor, are you still coding this way?

    ReplyDelete
    Replies
    1. In regard to combining this style with naming conventions, how are you avoiding class/variable name collisions in your code ie. `driver drv;` or `driver_c driver;` or `driver m_driver;`? Why have you chosen the convention you chose? And if you are choosing `driver_c driver;` what suffix are you using for constraints?

      Delete
    2. I prefer `driver drv;`. If the shortened name is too ugly, I'll do `some_package::driver driver;`. Sometimes it's also acceptable to shadow the class name and do `driver driver;`, because you don't need to use that class inside that scope anymore.

      For private fields, I'd go with `driver m_driver;` or, since I started writing more Python, `driver _driver;`.

      Under no circumstances would I put suffixes on class names (`driver_c`).

      Delete
    3. For me this is a tough choice because it is a choice of the least evil.

      `driver drv;` is good except it breaks the software practice of using unabbreviated dictionary words. On the other hand, this type of word shortening is extremely common place in our industry, RTL included.

      `driver driver;` I didn't know some compilers would allow this. I tried with VCS 2020.03 and it would not compile.

      `driver m_driver;` This works for keeping private fields from colliding with the class, but is not of help for non-private fields. In addition software practices are against this type of thing. https://stackoverflow.com/questions/13018189/what-does-m-variable-prefix-mean/26563953#26563953

      `driver_c driver;` The postfix on class names, or on any type for that matter, is in a small way code duplication ie. we put the class key word and _c. However these types of suffixes are all over the place for other types we create in SystemVerilog. https://stackoverflow.com/questions/13018189/what-does-m-variable-prefix-mean/26563953#26563953

      I think if our convention was to use capitals in class names like some other languages, `Driver driver;`, we'd be good to go, but alas we don't. Since we do use suffixes elsewhere I'm personally leaning toward `driver_c driver;`. The nice thing is it works well in all circumstances ie. not just private variables, or when the abbreviated version of the word is still clear.

      I'm curious what is behind your strong feeling against `driver_c driver;`?

      Delete
    4. The link you mentioned relates to using 'm_' when not needed. In the back of my mind, I was thinking of situations where you get clashes between a function and a private field. Something like a class that has a 'some_property()' function and an internal field for the property. The internal field has to be called something other than `some_property`, so I usually use '_some_property'. (The idea is that `obj.some_property()` reads better than `obj.get_some_property()`.) If I had a `get_/set_` pair of functions, I'd also use `some_property` for the field, even if it is internal.

      `driver_c` is basically trading the prefix for a suffix (albeit a shorter one).

      Delete
    5. I also wanted to mention capitalization of class names in the first reply, but I forgot. I've been mulling it for a while now and it's probably time. If one rejects package prefixes, then it makes sense.

      Also, I'm guessing some of the fondness for snake case comes from VHDL, that is case insensitive. Also from C, which prefers snake case, but doesn't have the issue with types/instances (it has them with `structs`, but I don't know whether those were a later addition, after snake case was already established).

      Python uses both CamelCase and snake_case, so why not for SV as well?

      Delete
    6. For me, 'm_' is susceptible to having the same issue as unmaintained comments - they can get stale or otherwise be inaccurate giving the reader wrong information. This for sure happens; Have a look at this post from Jeremy who points out incorrect use of 'm_' even in UVM source code. https://verificationacademy.com/forums/uvm/guidelines-about-when-use-m-prefix

      For method/variable collisions, I like the software practice of making method names verb-like and variables noun-like.

      In regards to your statement "`driver_c` is basically trading the [package] prefix for a suffix", that is the case, but only when writing code outside of `driver_c`s package. If I'm using `my_pkg::driver driver;` inside of my_pkg, lets say in my_pkg::agent, it will not compile in at least VCS if not others. That means to be consistent we'd have to differentiate the class name from the variable name. ie. `my_pkg::Driver driver;` or `my_pkg::driver_c driver;`, etc (and we can of course drop my_pkg:: within the my_pkg package).

      At this moment, I am leaning toward `driver_c` over `Driver`. Though I am a lover of Ruby which also uses CamelCase, `driver_c` seems a bit more in line with today's SV conventions. The only issue I have with it is it collides with my constraint suffix. Bah!

      Delete