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.