class_declaration
::=
{ attribute_instance } [ virtual ] class
[ lifetime ] class_identifier [ parameter_port_list ]
[ extends class_identifier
[ parameter_value_assignment ] ] ; [ timeunits_declaration ]
{ class_item }
endclass [ : class_identifier]
class_item ::=
{ attribute_instance } class_property
| { attribute_instance }
class_method
| { attribute_instance }
class_constraint
| { attribute_instance } type_declaration
| {
attribute_instance } class_declaration
| {
attribute_instance } timeunits_declaration21
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 class properties, and its subroutines
are called methods, both are members of the class. The class properties and methods, taken together, define
the contents and capabilities of some kind of object.
The data fields of an object can be used by qualifying class property names with an instance name. Using the
earlier example, the commands for the Packet object p can be used as
follows:
An object’s methods can be accessed using the same syntax used to
access class properties:
Section 11.7
The new operation is defined as a function with no
return type, and like any other function, it must be nonblocking. Even though
new does not specify a return type, the left-hand side of the assignment
determines the return type.
Class properties that include an initializer in their declaration
are initialized before the execution of the user-defined class
constructor. Thus, initializer values can be overriden by the class constructor
11.8 Static class
properties
The previous examples have only declared instance class properties. Each instance of the class (i.e.,
each object of type Packet), has its own copy
of each of its six variables. Sometimes only one version of a variable is
required to be shared by all instances. These class properties are created
using the keyword static. Thus, for
example, in a case where all instances of a class need access to a common file
descriptor:
Now, fileID shall be created and initialized once. Thereafter, every Packet object
can access the file descriptor in the usual way:
Packet
p;
c =
$fgetc( p.fileID );
Note
that static class properties can be used without creating an object of that
type.
Methods can be declared as static. A static method is subject to all the class scoping and access
rules, but behaves like a regular subroutine that can be called outside the
class, even with no class instantiation. A static method has no access to
non-static members (class properties or
methods), but it can directly access static class properties or call static
methods of the same class. Access to non-static members or to the special this
handle within the body of a static method is illegal and results in a compiler
error. Static methods cannot be virtual.
The this keyword is used to
unambiguously refer to class properties or
methods of the current instance. The this keyword denotes a predefined object handle that refers to the
object that was used to invoke the subroutine that this is used within. The this keyword shall only be used within
non-static class methods, otherwise an error shall be issued. For example, the
following declaration is a common way to write an initialization task:
The x is now both a
property of the class and an argument to the function new. In the function new, an unqualified reference to x shall be resolved by looking at the
innermost scope, in this case the subroutine argument declaration. To access
the instance class property, it is qualified
with the this keyword, to refer
to the current instance.
The last statement has new executing
a second time, thus creating a new object p2, whose class
properties are copied from p1. This is known as a shallow copy. All of
the variables are copied across: integers, strings, instance handles, etc.
Objects, however, are not copied, only their handles; as before, two names for
the same object have been created. This is true even if the class declaration
includes the instantiation operator new:
Several things are noteworthy. First, class
properties and instantiated objects can be initialized directly in a class
declaration. Second, the shallow copy does not copy objects. Third, instance
qualifications can be chained as needed to reach into objects or to reach
through objects:
To refer to a class property of Packet, the variable packet_c needs to be referenced.
Now, all of the methods and class
properties of Packet are part of LinkedPacket—as if they were defined in LinkedPacket—and LinkedPacket has additional class properties and
methods.
In this case, references to p access
the methods and class properties of the Packet class. So, for example, if class properties and methods in LinkedPacket are overridden, these overridden members
referred to through p get the original
members in the Packet class. From p, new and all overridden
members in LinkedPacket are now hidden.
So far, all class properties and methods have been made available
to the outside world without restriction. Often, it is desirable to restrict
access to class properties and methods from outside
the class by hiding their names. This keeps other programmers from relying on a
specific implementation, and it also protects against accidental modifications
to class properties that are internal to the
class. When all data becomes hidden—being accessed only by public
methods—testing and maintenance of the code becomes much easier.
In SystemVerilog, unqualified class
properties and methods are public, available to anyone who has access to the
object’s name.
A member identified as local is available only to methods inside the class. Further, these
local members are not visible within subclasses. Of course, non-local methods
that access local class properties or methods
can be inherited, and work properly as methods of the subclass.
A protected
class property or method has all of the characteristics of a local member, except that it can be
inherited; it is visible to subclasses.
Note that within the class, a local method or class property of the class can be referenced, even if
it is in a different instance. For example:
class Packet;
local
integer i;
function
integer compare (Packet other);
compare = (this.i == other.i);
endfunction
endclass
A strict interpretation of encapsulation might say that other.i should not be visible inside of this
packet, since it is a local class property being
referenced from outside its instance. Within the same class, however, these
references are allowed. In this case, this.i shall be compared to other.i and the result of the logical comparison returned.
Class members can be identified as either local or protected; class
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 can 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.
11.18 Constant Class
Properties
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: global constants and
instance constants.
Global constant class properties
are those that include an initial value as part of their declaration. They are
similar to other const variables in that
they cannot be assigned a value anywhere other than in the declaration.
Abstract classes can also have virtual methods. Virtual methods are a basic polymorphic construct. A virtual method overrides a method in all the base classes, whereas a normal method only overrides a method in that class and its descendants. One way to view this is that there is only one implementation of a virtual method per class hierarchy, and it is always the one in the latest derived class. Virtual methods provide prototypes for subroutines, all of the information generally found on the first line of a method declaration: the encapsulation criteria, the type and number of arguments, and the return type if it is needed. Later, when subclasses override virtual methods, they must follow the prototype exactly. Thus, all versions of the virtual method look identical in all subclasses:
virtual
class BasePacket;
virtual
protected function integer send(bit[31:0]
data);
endfunction
endclass
class
EtherPacket extends BasePacket;
protected function integer send(bit[31:0]
data);
//
body of the function
...
endfunction
endclass
Because classes and other scopes can have the same identifiers,
the scope resolution operator uniquely identifies a member of a particular
class. In addition, to disambiguating class scope identifiers, the :: operator also allows access to static
members (class properties and methods) from
outside the class, as well as access to public or protected elements of a
super-classes from within the derived classes.
The scope resolution operator enables:
—
Access to static public members (methods and class properties) from outside the class hierarchy.
The
out of block method declaration must match the prototype declaration exactly;
the only syntactical difference is that the method name is preceded by the
class name and scope operator (::).
Out of block declarations must
be declared in the same scope as the class declaration.
class vector #(parameter int size = 1;);
bit [size-1:0] a;
endclass
class stack #(parameter type T = int;);
local T items[];
task push( T a ); ... endtask
task pop( ref T a ); ... endtask
endclass
class vector #( parameter int size = 1;);
bit [size-1:0] a;
static int count = 0;
function void disp_count();
$display( "count: %d of size %d",
count, size );
endfunction
endclass
To avoid having to repeat the specialization either in the
declaration or to create parameters of that type, a typedef should be used:
typedef vector#(4) Vfour;
typedef stack#(Vfour) Stack4;
Stack4 s1,
s2; // declare objects of type
Stack4
A parameterized class can extend
another parameterized class. For example:
class C #(parameter type T = bit); ... endclass // base class
class D1 #(parameter type P = real) extends C; // T
is bit (the default)
class D2 #(parameter type P = real) extends C #(integer); // T is integer
class D3 #(parameter type P = real) extends C #(P); // T
is P
Class D1 extends the base class
C using the base class's default type (bit) parameter. Class D2 extends the
base class C using an integer parameter. Class D3 extends the base class C
using the parameterized type (P) with which the extended class is parameterized.
typedef class C2; // C2 is declared to be of type class
class C1;
C2 c;
endclass
class C2;
C1 c;
endclass