SystemVerilog introduces an
the object-oriented class type
system framework. Classes allow objects to be dynamically
created, and deleted, to be assigned
to, and to be accessed via object handles., which Object handles provide a safe pointer-like mechanism to
the language. Classes offer With inheritance and abstract type modeling classes, this framework which brings the advantages of C function
pointers with none of the type-safety problems, thus, bringing true
polymorphism into Verilog.
A class is a type that includes data and subroutines (functions and tasks) that operate on that data. A
class’s data is referred to as properties, and its subroutines are
called methods, both are members of the class. The properties and
methods, taken together, define the contents and capabilities of some kind of
object.
class
Packet ;
// data or class properties
bit
[3:0] command; // data portion
bit
[40:0] address;
bit
[4:0] master_id;
integer
time_requested;
integer
time_issued;
integer
status;
// initialization
function
new(); // initialization
command = IDLE;
address = 41’b0;
master_id = 5’bx;
endfunction
// methods
// public access entry points
task
clean();
command = 0; address = 0; master_id
= 5’bx;
endtask
// public access entry points
task
issue_request( int
delay );
// send
request to bus
endtask
function
integer current_status();
current_status = status;
endfunction
endclass
The previous section only provided the
definition of a class Packet. That is a new, complex data type, but one
can’t do anything with the class itself. First, one needs to create an instance
of the class, a single Packet object. The first step is to create a variable
that can hold an object handle:
A class defines a data type. An object is an
instance of that class. An object is used by first declaring a variable of that
class type (that holds an object handle) and then creating an object of that
class (using the new function) and assigning it to the variable.
Packet
p; // declare a variable of class Packet
p = new; // initialize the variable to a new
allocated object of the class Packet
The variable p is said to hold an object
handle to an object of class Packet.
Nothing has been created yet. The
declaration of p is simply a variable that can hold a handle of a Packet
object. For p to refer to something, an instance of the class must
be created using the new function.
Packet p;
p = new;
Move the last row of the table to the first row and shift
the first 3 rows right.
Note that new
is now being used in two very
different contexts with very different semantics. The variable declaration creates an object of class Packet. In the course of creating this instance, the new
function is invoked, in which any specialized
initialization required may be done. The new new
function task is
also called the class constructor.
The conventions for arguments are the same as for
procedural subroutine calls, including such as the use of default
arguments.
11.98.1
Static methods
There are times when one needs to unambiguously refer
to properties or methods of the current instance. The this class property (predefined) is used to refer to the object
that was used to invoke the subroutine that this is used within (it must only be used within class methods).
For example, the following declaration is a common way to write an
initialization task:
function
integer test;
B b1
= new; //
Create an object of class B
B b2
= new b1; //
Create an object that is a copy of b1
b2.i
= 10; // i
is changed in b2, but not in b1
b2.a.j
= 50; // change a.j, shared by both b1 and
b2
test = b1.i; //
test is set to 1 (b1.i has not changed)
test = b1.a.j; //
test is set to 50 (a.j has changed)
endfunction
The super keyword is used from within a derived class to refer
to members properties of
the parent class. It is necessary to use super to access members properties of a
parent class when those members properties are overridden by the derived class.
The member property may
be a
member declared a level up or be a member inherited by
the class one level up. There is no way to reach higher (for example, super.super.count is not allowed).
Subclasses (or derived
classes) are classes that are extensions of the current class. Whereas superclasses (parent classes or
base classes) are classes that the current class is extended
from, beginning with the original base class.
When used with object handles, $cast() checks the
hierarchy tree (super and subclasses) of the source_expr
to see if it contains the class of dest_handle dest_var. If it
does, $cast() does the assignment. Otherwise the error handling is
as described in Section 3.15.
When a subclass is instantiated, one of the system’s first
actions is to invoke the
class method new() is
invoked. The first, implicit action new() takes, before any code defined in the function is evaluated, is to invoke the new() method of its superclass, and so on up the inheritance
hierarchy. Thus, all the constructors are called, in the proper order,
beginning with the base class and ending with the current class.
In SystemVerilog, unqualified unlabeled properties
and methods are public, available to anyone who has access to the object’s
name.
A strict interpretation of encapsulation might say
that other.i should not be visible inside of this
packet, since it is a local property being referenced from outside its
instance. Within the same class, however, these references are allowed. In this
case, this.i will be compared to other.i
and the result of the logical comparison will be returned.
Class members can be
identified as either local or protected; properties can be further defined as const, and methods can be defined as virtual. There is no predefined ordering for specifying
these modifiers; however, they may only appear once per member. It shall be an
error to define members to be both local and protected, or to duplicate any of the other modifiers.
Class properties can be made read-only by a const
declaration like any other
SystemVerilog variable. However, because class objects are dynamic objects, class
properties allow two forms of read-only variables: Gglobal constants and Iinstance constants.
The first step is to create the base class
that sets out the prototype for these subclasses. Since the base class is not intended to be instantiated,, it can be made it can be made abstract by specifying by specifying the
class to be virtual:
Polymorphism allows one to use a variable in the superclass to hold
subclass objects, and to reference the methods of those subclasses directly
from the superclass variable. As an example, consider
the base class for the Packet objects,
BasePacket. Assuming that it defines, as virtual functions, all of the public
methods that are to be generally used by its subclasses, methods such as send,
receive, print, etc. Even though BasePacket is abstract, it can still be used to declare a
variable:
EtherPacket ep = new; // extends BasePacket
TokenPacket tp = new; // extends BasePacket
GPSSPacket gp = new; // extends EtherPacket
packets[0] = ep;
packets[1] = tp;
packets[2] = gp;
Editor’s Note: This new operator needs to be
added to the operator precedence table and other sections describing operator
rules (signed/unsigned/2-state/4-state/real operands, etc.).
class
Base;
typedef
enum {bin,oct,dec,hex}
radix;
static
task print( radix r, integer n ); ... endtask
endclass
...
Base b
= new;
int
bin = 123;
b.->print( Base::bin, bin
); // Base::bin and bin are different
Base::print( Base::hex, 66 );
It is convenient to be able to move method definitions
out of the body of the class declaration. This is done in two steps. Declare,
within the class body, the method prototypes—whether it is a function or task,
any hiding encapsulation qualifiers attributes (local, protected, or virtual), and the full argument specification plus the extern
qualifier. The extern
qualifier indicates that the body of
the method (it’s its implementation) is to be found outside the
declaration. Then, outside the class declaration, declare the full method—like
the prototype but without the hiding encapsulation
qualifiers attributes—and, to tie the
method back to its class, qualify the method name with the class name and a
pair of colons:
Editor’s
Note: Verilog syntax is “int static”. Is the “static int” above correct? NOTE: The Co-design SystemSim
simulator allows both forms. I submitted a request to the BC committee to
clarify if SystemVerilog was intended to also allow both forms. I do not know
the result of that request.
4) SystemVerilog objects form the basis of an
Object-Oriented type system framework that provides true polymorphism. Class inheritance,
abstract classes, and dynamic casting are powerful mechanisms that go way
beyond the mere encapsulation mechanism provided by structs.
In this example, the main process (the one that forks
off the two tasks) doesn’t know when the two processes might be done using the
object obj.
Similarly, neither task1 nor task2 knows when any of the other two processes will no
longer be using the object obj. It is evident from this simple example that no
single process has enough information to determine when it is safe to free the
object. The only two options available to the user are (1) play it safe and
never reclaim the object, or (2) add some form of reference count that can be
used to determine when it might be safe to reclaim the object. Adopting the
first option will cause the system to quickly run out of memory. The second
option places a large burden on users, who, in addition to managing their testbench test-bench,
must also manage the memory using less than ideal schemes. To avoid these
shortcomings, SystemVerilog manages all dynamic memory automatically. Users no
longer need to worry about dangling references, premature deallocation,
or memory leaks. The system will automatically reclaim any object that is no
longer being used. In the example above, all that users do is assign null
to the handle obj when they
no longer need it. Similarly, when an object goes out of scope the system
implicitly assigns null to
the object.