How does " virtual" keyword work in systemverilog? - system-verilog

I'm trying to understand 'virtual' keyword along with function.
I've got some experiment as the below,
class A ;
function void disp ();
$display(" Non-Virtual from A ");
endfunction
virtual function void vdisp ();
$display(" Virtual from A ");
endfunction
endclass
class EA extends A ;
function void disp ();
$display(" Non-Virtual from EA ");
endfunction
virtual function void vdisp ();
$display(" Virtual from EA ");
endfunction
endclass
module main ;
function void disp( A a);
a.disp();
a.vdisp();
endfunction
A my_a;
EA my_ea;
initial
begin
my_a = new();
my_ea = new();
disp(my_a);
disp(my_ea);
end
endmodule
and I've got the below message from code,
Non-Virtual from A
Virtual from A
Non-Virtual from A
Virtual from EA
But my expectation is that should be like this,
Non-Virtual from A
Virtual from A
Non-Virtual from EA
Virtual from EA
What does " vitual" keyword work in systemverilog?
Would you please explain why do I get this result? and how to resolve this problem?

You have constructed two object, one of type A whose handle is stored in my_a, and the other of type EA whose handle is stored in my_ea. When you call disp(my_ea), you are upcasting the EA handle to the function argument class variable a of type A.
When you call a non-virtual function, it uses the fixed class type A of the class variable a to choose which method gets called. So function A::disp gets called.
When you call a virtual function, the dynamic type of the class handle EA stored in the class variable a is used to choose which method gets called. So EA::disp gets called.

In other words: virtual function is a special type of function that, when called, executes the “most” child method that exists between the parent and child classes. This property is also known as polymorphism. The child method is called when the signature (name, parameter types, and whether the method is a constant) and return type of the child method match the signature and return type of the parent class method. Such methods are called overrides (or "overridden methods"). Because of class EA extends A , you got Virtual from EA ,as EA `s method most child)

Related

Is it possible to call new in SystemVerilog outside an assign statement?

This is how I usely call new in SystemVerilog:
class A;
endclass
A a = new();
But sometimes, I don't need a local object, I just want to send it directly to a function taking an A. Is there a way to call the new function explicitly here:
function use_a(A obj);
endfunction
use_a(new()); // <--- How to write this call to specify which new to call?
use_a(A::new()); // <--- new not expected here :(
Unfortunately, SystemVerilog's syntax does not allow this. The special new method is not a static method, and a class handle has to exist in some variable because of the way class memory management is defined. You could get around this by wrapping new around a static method:
class A;
static function A create();
create = new();
endfunction
endclass
...
use_a(A::create());
BTW, the UVM has create methods in the BCL and you almost never need to call new() directly.

Store reference to array/queue in SystemVerilog

I'd like to store a reference to an array/queue inside a class. It's doesn't seem possible to do this, though.
I'd like to do something like this:
class some_class;
// class member that points to the 'q' supplied as a constructor arg
??? q_ref;
function new(ref int q[$]);
this.q_ref = q;
endfunction
endclass
If q_ref is merely defined as int q_ref[$], then the assignment operator will create a copy, which isn't what I want. I'd like changes in 'q' to be visible inside the class.
Is there some hidden section in the LRM that shows how this can be done?
I'm not looking for the obvious "you have to wrap the array/queue in a class answer", but for something that allows me to interact with code that uses native arrays/queues.
There are only three variable types in SystemVerilog that can store references: class, event, and virtual interfaces variables.
You have to wrap the array/queue as a member in a class object. Then, any method of that class can be used in an event expression. Any change to a member of the class object causes a re-evaluation of that method. See the last paragraph and example in section 9.4.2 Event control of the 1800-2012 LRM.
So, the only solution for you would be to wrap the queue in a class. The latter is always assigned by a reference, as in this example:
class QueueRef #(type T = int);
T queue[$];
function void push_back(T t);
queue.push_back(t);
endfunction // push_back
endclass // Queue
class some_class;
QueueRef q_ref;
function new(QueueRef q);
this.q_ref = q;
endfunction
endclass
program test;
QueueRef q = new;
some_class c = new (q);
initial begin
q.push_back(1);
q.push_back(2);
$display(c.q_ref.queue);
end
endprogram // test

What is the purpose the 'new' and 'virtual' in systemverilog?

I'm trying to learn about SystemVerilog. While reading about it, I came across the following code, which I cannot fully understand:
Test1.
class A ; 
task disp();
$display(" This is class A "); 
endtask 
endclass 
class EA extends A ; 
task disp (); 
$display(" This is Extended class A "); 
endtask 
endclass 
program main ; 
EA my_ea; 
A my_a; 
initial 
begin 
my_a.disp(); 
my_a = my_ea; 
my_a.disp(); 
end 
endprogram 
Test2.
class A ; 
virtual task disp (); 
$display(" This is class A "); 
endtask 
endclass 
class EA extends A ; 
task disp (); 
$display(" This is Extended class A "); 
endtask 
endclass 
program main ; 
EA my_ea; 
A my_a; 
initial 
begin 
my_a = new(); 
my_a.disp(); 
my_ea = new(); 
my_a = my_ea; 
my_a.disp(); 
end 
endprogram 
I have some questions about the test1 code above. There is a call to some 'new' function, but the implementation of that is not provided anywhere. How can this code compile and run then?
Also in the test2, you can see the 'virtual' keyword. I do not understand the reason behind using 'virtual'. Can you please explain why do we have to use 'virtual' in this context?
update
I'd like to implement the example code from Greg.
But I've got some problem as the below
Chronologic VCS (TM)
Version J-2014.12-SP1-1 -- Wed Aug 8 08:33:23 2018
Copyright (c) 1991-2014 by Synopsys Inc.
ALL RIGHTS RESERVED
This program is proprietary and confidential information of Synopsys Inc.
and may be used and disclosed only as authorized in a license agreement
controlling such use and disclosure.
Parsing design file 'design.sv'
Parsing design file 'testbench.sv'
Error-[SE] Syntax error
Following verilog source has syntax error :
"testbench.sv", 21: token is '('
function(A a);
^
1 error
CPU time: .073 seconds to compile
Exit code expected: 0, received: 1
Done
The new keyword is a constructor, it creates the object. Since new is not defined it is inferring the default constructor:
function new();
endfunction
Objects must be constructed before you call any of there methods. Test1 should through a null pointer error because you call an object's method that hasn't been constructed.
The virtual keyword and concept is the same in C++, Java, etc. There are plenty of explanations of this already answered on the virtual topic and polymorphism, such as : Why do we need virtual functions in C++?
In a nutshell a parent handle pointing to a child object can execute the object's method if it is virtual. Best way understant this is the create a class and child class that has both a virtual and non-virtual methods. Example:
module main ;
class A ;
function void disp ();
$display(" Non-Virtual from A ");
endfunction
virtual function void vdisp ();
$display(" Virtual from A ");
endfunction
endclass
class EA extends A ;
function void disp ();
$display(" Non-Virtual from EA ");
endfunction
virtual function void vdisp ();
$display(" Virtual from EA ");
endfunction
endclass
function void disp(A a);
a.disp();
a.vdisp();
endfunction
EA my_ea;
A my_a;
initial
begin
my_a = new();
my_ea = new();
disp(my_a);
disp(my_ea);
my_a = my_ea;
disp(my_a);
end
endmodule

system verilog : Overridden members system verilog classes

class my_a;
int member1 = 1;
endclass
class my_ea extends my_a;
int member1 = 2;
endclass
Now when I do
my_a A;
my_ea EA;
EA =new();
A=EA;
EA = new(); has given handle to object of type my_ea to class variable EA.
A=EA; passes the same handle (pointer value which points to object of my_ea) to A. So, A.member1 should refer to value 2.
But it refers to value 1. Why?
So far, System-Verilog does not allow overriding of class variables. Only virtual methods can be overridden.
There is nothing like virtual for class members, so parent class can never directly access them. When using class_object.member, the particular class is referred to. Henceforth, this is not possible.
You cannot redefine an existing member by extending a class. You should use virtual methods to access (get/set) them. For instance, I added "get_member1" function to your code, and it returns 2 when called from a base class handle as you wanted.
class my_a;
int member1 = 1;
virtual function int get_member1();
return member1;
endfunction
endclass
class my_ea extends my_a;
int member1 = 2;
virtual function int get_member1();
return member1;
endfunction
endclass
module tb;
initial begin
my_a A;
my_ea EA;
EA =new();
A=EA;
$display("%0d", A.get_member1());
end
endmodule
You can similarly define "set_member1(int value)" function and use it to change its value.
In your case A.member1 should return the original member of its own class. When you are overriding a class members, you are basically hiding the overridden members. Super/base class can never access overridden member in its subclass.
As far as I know, only method identified with virtual, randomize() function and class constraint can be overridden without hiding them from the base class - thus they allow base class to reference to them (polymorphism)
For more info, please find here IEEE 1800-2012 in section 8.14 Overridden members.

Does UVM support nested/inner classes?

The code guideline for our verification environment is one class per file.
Sometimes a uvm_object is only needed by 1 other uvm_component, so, following object-oriented theory, we should use nested/inner classes.
Nested classes are fully supported by SystemVerilog. However, are they supported by UVM?
Is it possible to compile something like the following:
class inception_level_1 extends uvm_test;
`uvm_component_utils(inception_level_1)
function new(string name = "inception_level_1", uvm_component parent = null);
super.new(name, parent);
endfunction
class inception_level_2 extends uvm_object;
int a;
`uvm_object_utils_begin(inception_level_2)
`uvm_field_int(a, UVM_DEFAULT)
`uvm_object_utils_end
function new(string name = "inception_level_2");
super.new(name);
endfunction
endclass
endclass
Currently the above code gives a compile error:
** Error: testbench.sv(20): (vlog-2889) Illegal to access non-static method 'uvm_report_warning' outside its class scope.
Full code example here: http://www.edaplayground.com/x/3r8
SystemVerilog has packages, which is the preferred mechanism to "hide" class declarations from other packages.
You will have problems using the field macros, or anything else that tries to reference identifiers from inside the inner class that are defined with the same name in both the global uvm_pkg and the outer class. All the uvm_report_... methods are defined in both because uvm_component is extended from uvm_report_object, and uvm_report_... is in the global uvm_pkg.
You will also have problems using the factory with nested classes. Only the outer class will be able to provide overrides by type, but string based overrides by name are global. So even if you nested the inner class, scopes other than the outer class will be able to provide it as an override by string name.
I changed the code to remove the field macros and this runs. So it seems like this is supported if you can give up the field automation macros: http://www.edaplayground.com/x/i5
class inception_level_1 extends uvm_test;
`uvm_component_utils(inception_level_1)
function new(string name = "inception_level_1", uvm_component parent = null);
super.new(name, parent);
endfunction
class inception_level_2 extends uvm_object;
int a;
`uvm_object_utils(inception_level_2)
function new(string name = "inception_level_2");
super.new(name);
endfunction
endclass
endclass
in general it does work. however there are situations where UVM uses shortcuts which conflict with the class-in-class scenario. examples are
string based factory (inception_level_2 can only be registered once despite that foo:inception_level_2 and bla::inception_level_2 would be different classes)
name lookup collision (here for uvm_report_warning which should goto uvm_pkg::uvm_report_warning and not to the enclosing class uvm_component::uvm_report_warning)
... etc