A program in a class is also called a method, that is, an internal task or function defined within the scope of the class. The following example defines the display () method for the Transaction class. System Verilog will call the correct display () method according to the type of handle.
class Transaction; bit[31:0]addr,crc,data; function void display(); $display("@%0t:TR addr=8h,crc=sh",Stime,addr,crc); $write("\tdata[0-7]="); foreach(data[i])$write(data[i]); $display(); endfunction endclass initial begin t=new();//Create a Transaction object t.display();//Method calling Transaction end
Methods in the class use automatic storage by default, so you don't have to worry about forgetting to use the automatic modifier.
Define methods outside the class
In System Verilog, you can put the prototype definition (method name and parameters) of the method inside the class, and the program body (procedure code) of the method is defined after the class.
The following is an example of how to create an out of block declaration. Copy the first line of the method, describe the method name and parameters, and then add the keyword extern at the beginning. Then move the whole method to the back of the class definition, and add the class name and two colons (:: scope operator) before the method name. The class in the above example can be defined as follows.
// class Transaction; bit[31:0]addr,crc,data; extern function void display(); endclass function void Transaction::display(); $display("@% 0t:Transaction addr=%h,crc=8h", Stime,addr,crc); $write("\tdata[o-7]="); foreach (data[i])Swrite(data[i]); $display(); endfunction
Note: there are two common errors:
1. It is a common coding error that the prototype definition of a method does not match the content. System Verilog requires that except for one more class name and scope operator, the prototype definition is consistent with the method definition outside the block.
2. Another common mistake is to forget to write the class name when declaring a method outside the class. As a result, its scope is one level higher. When a task tries to access class level variables and methods, the compiler will report an error.
A scope is a code block, such as a module, a program, task, function, class, or a begin end block. A for and foreach loop automatically creates a block, so the subscript weight can be declared and created as a local variable of the loop scope.
A new feature in system Verilog is that variables can be declared in a begin end block without a name, such as defining index variables in a for loop
The class should be defined in a package outside the program or module. This should be observed by all test platforms. Temporary variables can be defined somewhere in the innermost part of the test platform.
If an undeclared query is used in a block and there happens to be a variable with the same name in the block, the class will use the variable in the block without giving any warning.
The following example moves the class to a package, so the class can't see the program level variables, so it won't call it unintentionally.
//Move the class into package to find program errors package Mistake; class Bad; logic[31:0]data; //i is not defined and will not be compiled function void display; for(i=0;i< data.size();i++) $display("data[%0d]=%x",i,data[i]); endfunction endclass endpackage
What is this
When using a variable name, SystemVerilog will first look in the current scope, and then look in the upper scope until the variable is found. But what if you want to explicitly reference class level objects in the deep underlying scope of a class. In the following example, "this" assigns oname to the class level variable oname
//Use this pointer to point to class level variables class Scoping; string oname; function new(string oname); this.oname=oname;//Class variable oname = local variable oname endfunction endclass
Use another class in one class
By using a handle to an object, a class can contain an instance of another class. This is like in Verilog, including an instance of another module within one module to establish the hierarchy of the design. The purpose of such inclusion is usually to reuse and control complexity.
Understanding dynamic objects
Passing objects to methods
When passing an object to a method, you may need to read the value in the object and modify the value of the object. In either case, when calling a method, you pass the handle of the object rather than the object
When calling a method with a scalar variable (not an array or an object) and using the ref keyword, SystemVerilog passes the address of the scalar, so the method can also modify the value of the scalar variable. If the ref keyword is not used, System Verilog copies the value of the scalar into the parameter variable. Any change to the parameter variable will not affect the value of the original variable.
//Transfer the packet to a 32-bit bus task transmit(Transactiont); CBbus.rx data<=t.data; t.stats.startT=$time; ... endtask Transaction t; initial begin t=new(); //Allocate space for objects t.addr=42; //Initialization value transmit(t); //Passing objects to tasks end
In the above example, the initialization block first generates a Transaction object and calls the transmit task. The parameter of the transmit task is the handle to the object. By using handles, transmit can read and write values in objects. However, if transmit tries to change the handle, the initialization block will not get the result because the ref modifier is not used on the parameter. (just as ref passes other function parameters)
This note is compiled with reference to the green paper system verilog verification. It is only for learning and experience exchange. If infringement is involved, please inform me and I will deal with it as soon as possible.