Tuesday, October 7, 2014

A well known SystemVerilog limitation is that the same literal cannot appear in more enumerated types within a package (or more precisely within a scope).

Let's look at a concrete example. We'll assume that we're verifying a DUT that can receive data from the outside world, perform some mathematical operations on it and sends it back. We want to model the operations that our DUT performs and the best way to do that is by using two enumerated types:

The DUT doesn't continuously crunch data, so we want to add a literal for each enum to represent it not doing anything. Let's use the value NONE (I know that for math operations the value NOP would have been more appropriate, but please bear with me, I'm trying to illustrate a point). As discussed above, this code won't compile, because NONE is declared in both types.

What I've seen people do in this case is try to uniquify the names by adding either prefixes or suffixes. I, too, plead guilty to this. For example, the math_action_t type would contain the value NONE2, in order not to clash with the NONE from comm_action_t. This solution seems clumsy to me. Not only that, but if you're trying to connect to a VHDL DUT one "Big Three" simulator is going to complain because the literals don't exactly match (note: VHDL allows the same literal to be present in multiple types).

A very naïve solution would be to define each type in its own package. In the main package we would then import both of these packages:

We've solved the collision problem, because each type is now defined in its own scope. We can have our cake and eat it too! Or can we? Let's see what happens if we try to use the NONE literal in some procedural code:

Your simulator should, at this point, cowardly refuse to compile the code above, because the literal NONE was imported via wildcards multiple times and it's ambiguous. The correct way to do it is to qualify it with the appropriate packages:

This solution works as well as the first one. In some respects it's more elegant, but it's also clumsier. Creating a package for each enum will pollute our work library. Also, when using literals in our code, the values ADD and TX, for example, can be written as-is, but we have to write pkg1::NONE. This isn't uniform at all. In addition, think of what would happen if we had to add a new type to a third package that doubled up the value ADD. We'd have to go and scope all of the existing references to ADD with pkg2.

Let's take a step back and consider another situation. What if we want to model each area of functionality separately? What I mean is, instead of creating a big model class that can handle everything, let's assume that we can create a clean split between the communication side and the math side. In this case, each model would be an own class. This means that each class can just embed the enumerated variable definition:

Since each class represents a different scope, we don't have any problem with the definition of NONE. What we defined here for comm_model, for example, is just a variable called comm_action that can take any of the three values. We can use this as we would any enumerated variable. Hey, you know what else we can define inside a class? An actual type. I guess you already know why I made this little detour...

What if we mixed the two approaches and just defined each type in its own class instead of in its own package? Here's how this would look like:

Again, since each class is its own scope, we don't have any problem with collisions. Also, to prevent anyone from instantiating these classes, we define them virtual; their only purpose is to wrap the type definitions. If we want to use these wrapped enumerations, we have to scope them with their containing classes:

Notice that we also need to scope the enum literals. This solution is more uniform, in that it treats all literals the same (we have to scope all of them). It's also better encapsulated, since we only create extra class types inside our own package. It is, of course, more verbose, but we can't getting something for nothing.

C++ had the same problem with enumerated types, but the C++11 standard fixed that by adding a new construct, the enum class. This is basically the same thing as we just saw above, but it is a first class construct in the language. If you're curious about the topic, you can read more about it here.

I for one would like to see a similar enhancement to SystemVerilog in the future. What I would avoid, however, is calling it an enum class, as I think the word "class" carries a different connotation and would just confuse users (we anyway have the problem that the word "interface" has a double meaning). What might be practical though is to add scoping to the current construct and to allow the compiler to determine the type of a literal based on the context (à la VHDL or e). Here's what I mean:

In the code snippet above, when assigning a value to comm_action we can just omit the scope operator, because from the context we know that the left hand side of the expression is of type comm_action_t, so we would expect the right hand side to be of the same type. The situation is similar for the logical operator inside the if statement. If however we want to do a cast, we can't figure out from the context what enum the value NONE belongs to (as it could be multiply defined), so we have to use the :: operator. This solution would mean that code written in SV2012 could potentially be incompatible to the SV2099 version (that includes this proposal), but I would expect the occurrences of the third construct (the casting) to be far fewer than those of the first two constructs (where we can determine the type from the context).

In the meantime, our best bet is to just wrap enumerated types in virtual classes to avoid name collisions between literals. I hope you found this post useful. See you next time!

About

I am a Verification Engineer at Infineon Technologies, where I get the chance to work with both e and SystemVerilog.
I started the Verification Gentleman blog to store solutions to small (and big) problems I've faced in my day to day work. I want to share them with the community in the hope that they may be useful to someone else.