SystemVerilog Class Variables and Objects
How can you visualize the relationship between classes and objects in SystemVerilog? This is the first post in a series on Object-Oriented Programming in SystemVerilog and UVM. To best absorb this, you should know how to write a class in SystemVerilog. Let’s get started!
When you first learn OOP, it can be easy to mix up class variables and objects. I started when I was, so this was especially hard. Get ready to flex your brain muscles! How are objects like a neighborhood?
For a quick review of the terms in this article, see Dave’s excellent description.
Variables and values
In Verilog, a variable v has a value, 5, so I would say that v has the value 5, a direct relationship.
OOP has a two-step relationship between class variables, properties, and values. As you read this article, I encourage you to fire up your favorite editor, wrap these code examples with module-endmodule and simulate with your favorite tool, stepping through the code with the GUI. Don’t just lean back and read – lean forward and absorb what you see! A true engineer practices their craft and improves their skills.
Welcome to my neighborhood
The blueprint for a house says what the house will look like, with N doors and M windows. However, you can’t live in a blueprint – you need to construct the house, which occupies physical space. Here is a house with 1 door, 1 window, painted blue.
Build enough houses and you have a neighborhood of houses, each identical, but separate. You can have many identical houses so how do you tell them apart? In this image, each has a different color and address.
When you want to describe group of related features and functionality in SystemVerilog, such as a transaction, describe it with a class. The transaction has properties (variables) such as data and address, and methods (subroutines) that operate on the properties. A class encapsulates these properties and methods, describing what is in the transaction and how it operates. Think of the class as the blueprint for a transaction.
Construct an object
In the neighborhood above, you need to build a house as you can’t live in a blueprint. Likewise, a transaction class describes what it looks like, but you need to build a transaction object to provide space in memory for the address and data values. When you construct an object, the simulator allocates a chunk of memory to hold all the properties for a single class, such as a transaction.
Here is a SystemVerilog class for a TX transaction with data and an ID.
class Tx; bit [31:0] data; bit [23:0] id; endclass
A SystemVerilog object is stored in memory at a given address. In other languages you would refer to the object with pointer that holds its address. SystemVerilog uses a handle, which has both the address and the type, such as the Tx type. A class variable holds the handle. Here is an object (house) at address 1, and the class variable (mailbox) that points to it. (This is not the SystemVerilog mailbox type – just a rough dwelling analogy.)
Now declare the class variable t1 of type Tx.
Initially, t1 equals null, which means it does not point to any object. Construct a Tx object by calling the special function new().
t1 = new();
SystemVerilog allocates space for the Tx object and returns a handle to the object. The class variable t1 now holds that handle.
Because a handle includes the type of the object, you can only assign it to the appropriate class variable. You can’t assign a Tx handle to a Xbus class variable. This check for compatibility is why a SystemVerilog handles are called “type-safe”.
class Xbus; bit [31:0] data, addr; int id; endclass Xbus x1; initial x1 = t1; // Illegal code: mismatched types; won't compile.
One step beyond
Don’t confuse the class variable and the object. Construct a Tx object using the handle t1 and give it the ID 42.
Tx t1, t2; t1 = new(); t1.data = 2; t1.id = 42;
At this point you might be tempted to call the object “t1”. After all, you just set the value of data and id with the name “t1”. This relationship looks like:
t2 = t1; t2.data = 8; $display(t1.data); // Displays: 8
Two class variables now point to a single object. You changed the variable in the one object with the t2 class variable and printed with t1. The connection between the class variables and the object is through the handles.
Don’t call the object t1 or t2 – those are the class variable names. If you set t1 to null, t2 still has a handle to the object.
t1 = null; t2.data = 7;
What’s in a name?
How should you refer to an object? SystemVerilog doesn’t let you see the address where an object is stored in memory. I like to make a property such as an integer ID or string name. I might have a whole “neighborhood” of objects and need to tell them apart. I could call the above object, “42”.
A SystemVerilog variable is tightly connected to its value. In contrast, a class variable refers to an object, which has variables, a looser connection. More details in the next post!
Some SystemVerilog users, like myself, are lazy and use the term “handle” (type-safe pointer) to refer to a class variable (stores a handle).
Here is the next post in this series.
Interesting that you give objects IDs
Ruby does it without you asking when objects are created.
Would be a nice add to SystemVerilog.