System Verilog - Fundamentals of object-oriented programming

Posted by Trizen on Fri, 18 Feb 2022 20:03:47 +0100

1, Overview of classes

Handle (spatial pointer)
Object (storage space)

Transaction t1, t2;//Declare handle t1, t2
t1 = new();//Instantiate the object and assign its handle to t1
t2 = t1;//Assign the value of t1 to t2, that is, t1 and t2 point to the same object
t1 = new();//Instantiate the second object and give its handle to t1 
//At this point, t1 points to the second created object
//t2 points to the first object created
  1. SV automatically reclaims space when no handle points to an object.
  2. Handles can be used to create multiple objects or point back and forth to different objects.
  3. Different from hardware domains such as module, the default type of variables declared in class is dynamic variables. The life cycle begins with object creation and ends with object destruction.
    If the keyword static is used to declare a variable within a class, it is a static variable. Life begins in the compilation stage and runs through the whole simulation.
  4. Methods can be defined outside the class through the extern keyword.

2, Use of handle

When a method is called, the handle of the object is passed, not the object itself.
The new handle can be assigned to the object at any time when the program executes.

3, Copy of object

1. Concept

  1. Copying an object refers to first creating a new object (opening up a new space), and then copying the member variable value of the target object to the members of the new object, which makes the value of the member variable of the new object consistent with that of the target object, that is, the copy of the object is completed.
  2. For SV ordinary variable copy, it is sufficient to use the assignment operator "=". The copy of the object cannot be realized by "=", because this operation is the assignment of the handle, not the copy of the object.

2. Light copy and deep copy

Both shallow and deep copies are object copies, not handle copies.
Shallow copy is similar to a photocopy of the original object. The value of the original object is blindly copied to the destination object. If a class contains a handle to another class, only the objects at the highest level will be copied, and the objects at the lower level will not be copied.

class Transaction;
	bit[31:0]addr, crc, data[8];
	static int count = 0;
	int id;
	Statistics stats;//Declare a handle to the Statistics class object in the Trasaction class
	function new;
		stats = new();
		id = count++;
	endfunction
endclass

Transaction src, dst;
initial begin
	src = new();//Create a Transaction object
	src.stats.startT = 42;
	dst = new src;//Copy src to dst with new operator
	dst.stats.startT = 96;//Change stats of dst and src
	$display(src.stats.startT);
end

As shown in the above figure, when the new function is called to copy, the Transaction object (data variable id and Statistics handle value stats) is copied, but the Statistics object value startT is not copied. The two handles point to the same memory space, so modifying startT with dst will affect the src value.

When using deep copy, you can copy the contents of all member variables in the object and instances of other classes nested in the object by defining the copy method yourself.

class Transaction;
	bit[31:0]addr, crc, data[8];
	static int count = 0;
	int id;
	Statistics stats;//Declare a handle to the Statistics class object in the Trasaction class
	function new;
		stats = new();
		id = count++;
	endfunction
	function Transaction copy;
		copy = new();
		copy.addr = addr;
		copy.crc = crc;
		copy.data = data;
		copy.stats = stats.copy();//Call the Statistics::copy function
		id = count++;
	endfunction
endclass

class Statistics;
	time startT,stopT;
	function Statistics copy;
		copy = new():
		copy.startT = startT;
		copy.stopT = stopT;
	endfunction
endclass

Transaction src, dst;
initial begin
	src = new();//Create a Transaction object
	src.stats.startT = 42;
	dst = src.copy();//Copy src to dst using deep copy
	dst.stats.startT = 96;//Only change the stats value of dst
	$display(src.stats.startT);
end

3. Usage suggestions

  1. The member copy function and new object generation function are divided into two methods, which makes it easier to reuse subclass functions and methods.
  2. In order to ensure that members of both parent and child classes can copy, the copy method is declared as a virtual method, and the principle of copying only domain members of this class is followed. The copy of members of the parent class should be completed by the copy method of the parent class.
  3. In the process of implementing the copy function, we should pay attention to the type conversion of the handle to ensure that the converted handle can access the class member variables.

4, Three elements of class

1. Packaging

Class, as a carrier, also has natural closed attributes, that is, its attributes and methods are encapsulated inside, and member variables will not be exposed to the outside directly.

  1. If no access type is specified, the default type of the member is public, and both subclasses and external members can access the member.
  2. If the specified access type is protected, only this class or subclass can access members, but the outside cannot.
  3. If you specify that the access type is local, only this class can access members, and neither subclasses nor outside can access members.

2. Succession

In terms of inheritance, class inheritance includes inheriting the member variables and member methods of the parent class. At the same time, the subclass can index the function with the same name of the parent class internally through super.
By default, if there is no super or this to indicate the scope, the variable is referenced according to the principle from near to far, that is:

  1. First, check whether the variable is a local variable defined inside the function.
  2. Secondly, it depends on whether the variable is a member variable defined by the current class.
  3. Finally, see whether the variable is a variable of the parent class or a lower class.

3. Polymorphism

Type conversion

1. Implicit conversion:
For some operations that do not require conversion, for example, the right side of the assignment statement is a 4-bit vector, while the left side is a 5-bit vector. The implicit conversion will first expand the bit width, and then do the assignment.
2. Explicit conversion:
It requires the intervention of operation symbols or system functions. Both static conversion and dynamic conversion are display conversion.
For static conversion, just put single quotation marks in front of the expression to be converted. This method does not check the converted value. If the conversion fails, we don't know.
Dynamic conversion requires the system function $cast(tgt, src) for conversion. There are two situations when using it:
(1) If the subclass handle is assigned to the parent handle, the compiler considers the assignment legal.
(2) If you assign a parent class handle to a child class handle, you need to use the $cast() function for conversion, otherwise a compilation error will occur.
reason:
(1) When a class is extended, all base class variables and methods will be inherited, so the base class handle can point to the extended object, because any reference using the base class handle is legal.
(2) If you do the assignment in the opposite direction, that is, copy a base class object to the handle of an extension class, it will fail. Because some attributes only exist in the extension class, the base class does not have them.
$ cast
(1) $cast() checks the type of object the handle points to, not just the handle itself. Once the source object and the target object are of the same type or the extension class of the target class, you can copy the address of the extension object from the base class handle to the handle of the extension object.
(2) $cast() returns 1 after successful execution, otherwise it returns 0.

Virtual method

(1) If the virtual modifier is used, SV will decide what method to call based on the type of object rather than the type of handle. In the last two lines of code below, tr points to an extension class object (BadTr), so the method called is BadTr::calc_crc.
(2) If the virtual modifier is not used, SV will call the method according to the type of handle rather than the type of object. This will cause the last statement in the following code to call the method transaction:: Calc of the parent class_ crc.

class Transaction;
	rand bit[31:0]src, dst, data[8];
	bit[31:0]crc;
	virtual function void calc_crc();
		crc = src^dst^data.xor;
	endfunction
endclass

class BadTr extends Transaction;
	rand bit bad_crc;
	virtual function void calc_crc();
		super.calc_crc();
		if(bad_crc)	crc = ~crc;
	endfunction
endclass
//Method call of class
Transaction tr;
BadTr bad;
initial begin
	tr = new();
	tr.calc_crc();//Call Transaction::calc_crc

	bad = new();
	bad.calc_crc();//Call BadTr::calc_crc

	tr = bad;//The base class handle points to the extension object
	tr.calc_crc();//Call BadTr::calc_crc
end

Suggestions for using virtual methods:
(1) Virtual methods can be declared only once through virtual.
(2) If you want to define virtual methods, you should try to define them in the underlying parent class. This is because if virtual is declared in the middle class of class inheritance relationship, only the call chain from the middle class to its subclasses will follow dynamic search, while the method call from the lowest class to the middle class will still follow static search.
(3) When defining a method for a parent class, if the method may be overwritten or inherited in the future, it should be declared as a virtual method.
(4) The inheritance of virtual methods also needs to follow the same parameters and return types.

5, Parameterized class

The use of parameterized classes can improve the reuse rate of code. In SV, you can add several data type parameters to the class and specify the type when declaring the class handle.

//Add parameters # (type T = int) during class definition, which means that when the class declares variables in the later stage,
//If the parameter type is not specified, the default is int.
class Stack # (type T = int);
...
endclass
initial begin
	Stack # (real)rStack;// Create a Stack that stores the real type
...
end

Topics: C++ OOP systemverilog