Using Obejcts
Using class in Sv is analogy to C++, refering to variables and routines in an object with the .
notation.
1 | BusTran b; |
In strict OOP, the only access to variables in an object should be through its public methods such as get()
and put()
.
While the get() and put() methods are fine for compilers, GUIs, and APIs, you should stick with public variables that can be directly accessed anywhere in the testbench.
Static Variables vs. Global Variables
Sometimes, we need a variable that is shared by all objects of a certain type.
Without OOP, we would probably create a global variable, which is used by one small piece of code, but is visible to the entire testbench.
Using Static variable
Example, creating static variable inside a class.
1 | class BusTran; |
Each time a new object is constructed, it is tagged with a unique value, and count is incremented.
SystemVerilog does not allow printing the address of an object, but we can create an ID field. Whenever tempting to make a global variable, consider making a class-level static variable.
Initializing static variables
Cannot do this in the class constructor, because it’s called for every single new object.
Intead, using the initial
block before the first object is constructed.
1 | class static; |
Class Routines (Method)
A routine (a.k.a. method) in a class is just a task or function defined inside the scope of the class.
Define Routines Outside of the Class
1 | class BusTran; |
Scoping rules
A scope is a block of code such as a module, program, task, function, class, or begin - end block. The for and foreach loops automatically create a block so that an index variable can be declared or created local to the scope of the loop.
Declare all your variables in the smallest scope that encloses all uses of the variable.
Using One class Inside Another
1 | class BusTran; |
Compilation order issue
Sometimes you need to compile a class that includes another class that is not yet defined. The declaration of the included class’s handle causes an error, as the compiler does not recognize the new type. Declare the class name with a typedef statement, as shown below.
1 | typedef class Statistics; |
Dynamic Obejcts
Use ref to pass the address of scalar variable (noarray, nonobject), so the routine can modify it.
1 | // Transmit a packet onto a 32-bit bus |
Modifying objects in flight
A very common mistake is forgetting to create a new object for each transaction in the testbench,
1 | task generator_bad(int n); |
So every time through the loop, generator_bad
changes the object at the same time it is being transmitted. When you run this, the $display
shows many addr values, but all transmitted BusTrans
have the same value of addr
. The bug occurs if transmit
stores the object and keeps using it even after transmit
returns. If your transmit
task does not keep a reference to the object, you can recycle the same object over and over.
1 | task generator_good(int n); |
You can make arrays of handles, each of which refers to an object, e.g., BusTran Barray[10]
.
Copying objects
You may want to make a copy of an object to keep a routine from modifying the original, or in a generator to preserve the constraints. You can either use the simple, built-in copy available with new
, or you can write your own for more complex classes.
1 | class BusTrain; |
This is a shallow copy, blindly transcribing values from source to destination. If the class contains a handle to another class, only the top level object is copied by new
, not the lower level one. Example as following,
1 | class BusTran; |
Note:
- it doesn’t call the custormized
new
function. - both objects point to the same
Statistics
object and both have the sameid
.
Writing your own simple copy function
1 | class BusTran; |
Note that you also need to write a copy for the Statistics class, and every other class in the hierarchy.