what does "virtual" mean when applied to a SystemVerilog interface? - system-verilog

What is the meaning of "virtual tinyalu_bfm" in the SystemVerilog code below? example:
uvm_config_db #(virtual tinyalu_bfm)::set(null, "*", "bfm", bfm);
would it make any difference if i omitted the virtual keyword? Just curious, because the usually oop meaning of virtual is applied only to classes or class members and here's an example where its applied to an interface being passed into a static function that's part of UVM package... just wanted to know why I need to call it virutal in this case and what its purpose is to make it virtual.
module top;
// UVM Framework
import uvm_pkg::*;
`include "uvm_macros.svh"
import tinyalu_pkg::*; //import all tb classes and types
tinyalu_bfm bfm();
// invoke APIs from uvm_pkg to start test...
initial begin
uvm_config_db #(virtual tinyalu_bfm)::set(null, "*", "bfm", bfm);
run_test();
end
endmodule : top
interface tinyalu_bfm;
byte unsigned A;
byte unsigned B;
bit clk;
bit reset_n;
initial begin
clk = 0;
forever begin
#10;
clk = ~clk;
end
end
task reset_alu();
reset_n = 1'b0;
#(negedge clk);
#(negedge clk);
reset_n = 1'b1;
start = 1'b0;
endtask : reset_alu
task send_op(input byte iA, input byte iB, input operation_t iop, output shortint alu_result);
// ...
endtask : send_op
endinterface : tinyalu_bfm
see here again... the interface object is declared virtual as well...why?
// FILE: random_test.svh
class random_test extends uvm_test;
// ...
virtual tinyalu_bfm bfm;
function new (string name, uvm_component parent);
super.new(name,parent);
if(!uvm_config_db #(virtual tinyalu_bfm)::get(null, "*","bfm", bfm))
$fatal("Failed to get BFM");
endfunction : new
task run_phase(uvm_phase phase);
//...
endtask : run_phase
endclass

SystemVerilog was created to be fully backward compatible with Verilog with the exception being newly reserved keywords. So SystemVerilog re-uses, or overloads existing keywords as much as possible to reduce keyword bloat. The virtual keyword in front of an interface name means you are declaring variable type that contains a handle to an actual interface instance, and not an actual interface instance,

Analog to a virtual interface in normal programming languages is a pointer or a reference (to the interface object). It is used as a reference in system verilog test bench components, passing it as function or task arguments or storing it in classes or other places.
System verilog is a huge language with a lot of ugliness in it. The virtual keyword is definitely overused, as in this case. There could have been a better choice.
|

I suggest the following define macro to make up for bad language choose by the designers of SystemVerilog to overload virtual keyword is a strange way:
`define REFERENCE virtual
module top;
// UVM Framework
import uvm_pkg::*;
`include "uvm_macros.svh"
import tinyalu_pkg::*; //import all tb classes and types
tinyalu_bfm bfm();
// invoke APIs from uvm_pkg to start test...
initial begin
uvm_config_db #(`REFERENCE tinyalu_bfm)::set(null, "*", "bfm", bfm);
run_test();
end
endmodule : top
interface tinyalu_bfm;
// ...
endinterface : tinyalu_bfm
see here again... the interface object is declared virtual as well...why?
// FILE: random_test.svh
class random_test extends uvm_test;
// ...
`REFERENCE tinyalu_bfm bfm;
function new (string name, uvm_component parent);
super.new(name,parent);
if(!uvm_config_db #(`REFERENCE tinyalu_bfm)::get(null, "*","bfm", bfm))
$fatal("Failed to get BFM");
endfunction : new
task run_phase(uvm_phase phase);
//...
endtask : run_phase
endclass

Related

SystemVerilog macro through task

How to send macros as parameters through a task?
In the testbench:
`define CPU1 tb.top.dual_processor_db_wrapper_i.dual_processor_db_i.cpu1.inst
`define CPU2 tb.top.dual_processor2_db_wrapper_i.dual_processor2_db_i.cpu2.inst
initial begin
fork
cpu_init(`CPU1);
cpu_init(`CPU2);
join
// Other stuff with `CPU1 and `CPU2
`CPU1.write_data(addr, 4, data, resp); // Works
end
task cpu_init(cpu);
cpu.por_srstb_reset(1'b1); // Does not work
// Other init stuff
endtask
Error when compiling:
ERROR: [VRFC 10-2991] 'por_srstb_reset' is not declared under prefix
'cpu'
The type of the `CPUs is unknown (to me). Perhaps Xilinx has a type for it, since it references their MPSoC VIP?
I assume por_srstb_reset and write_data are tasks or functions from Xilinx MPSoC VIP, but I'm not sure.
Xilinx documentation is very sparse
I general, it is possible to pass a macro as an argument to a task. However, it is not possible to pass a hierarchical reference as an argument to a task (it is illegal).
Operations on hierarchical references are very limited, in general.
Your task declaration is equivalent to the following:
task cpu_init (input logic cpu);
The cpu variable is a 1-bit type. So, the following is legal:
`define CPU1 1'b1
cpu_init(`CPU1);
The type of the argument must match between the declaration and the task call.
There is another approach to this problem by using bind and abstract/concrete classes
package pkg;
interface class abstract_init;
pure virtual task init; // prototype for each method you need
endclass
abstract_init lookup[string]; // database of concrete classes for each instance
endpackage
module bind_module #(string lookup_name);
import pkg::*;
class concrete_init implements abstract_init;
function new;
lookup[lookup_name] = this; // register this instance
endfunction
virtual task init;
processor.reset(); // upwards reference
endtask
endclass
concrete_init c = new; // each instance of this module gets registered in lookup
endmodule
`define cpu1 top.dut.cpu1
`define cpu2 top.dut.cpu2
// macro turns any argument into a quoted string
`define Q(arg) `"arg`"
module top;
dut dut();
bind `cpu1 bind_module #(.lookup_name(`Q(`cpu1))) b();
bind `cpu2 bind_module #(.lookup_name(`Q(`cpu2))) b();
initial fork
pkg::lookup[`Q(`cpu1)].init;
pkg::lookup[`Q(`cpu2)].init;
join
endmodule
module dut;
processor cpu1();
processor cpu2();
endmodule
module processor;
initial $display("Starting %m");
task reset;
#1 $display("executing reset on %m");
endtask
endmodule
This is described more detail in my DVCon paper: The Missing Link: The Testbench to DUT Connection.

SystemVerilog task that can force any signal in interface module

interface dut_if();
logic sig_a_i;
logic [1:0] sig_b_i;
endinterface
module tb();
dut_top dut(
.sig_a_i (vif.sig_a_i);
.sig_b_i (vif.sig_b_i);
);
dut_if vif();
endmodule
How to create a task() method inside the interface class such that I can easily call it within my test class to force/release any DUT signal I like?
class dut_testA_vseq extends dut_base_vseq;
...
virtual task body();
p_sequencer.vif.force_dut_signal(0);
endtask
endclass
I'm new to SV and I don't know how to write force_dut_signal() that can cater to any signal (single bit or a bus).
task force_dut_signal(logic? port_name, bit? force_val) begin
force port_name? = force_val;
endtask
The ? in the code snippet above are parts I don't know if possible or what.
Thanks in advance!
There is nothing in SystemVerilog that allows you to pass a hierarchical reference to a signal as a reference to a task/function argument. Inside your interface, you will need to create a function for each signal or group of signals you need to force. Then call that function from class.
BTW, always use functions instead of tasks for non-time-consuming procedures.
You could pass a signal by reference into your task. However the data types must match and you can't use a fork, join_any, join_none inside your task. This works for sig_a_i only:
interface dut_if();
logic sig_a_i;
logic [1:0] sig_b_i;
endinterface
module dut_top(
input logic sig_a_i,
input logic [1:0] sig_b_i);
endmodule
module tb();
dut_top dut(
.sig_a_i (vif.sig_a_i),
.sig_b_i (vif.sig_b_i)
);
dut_if vif();
function automatic force_1bit_logic(ref logic signal, input bit value);
signal = value;
endfunction
initial begin
#1;
force_1bit_logic(vif.sig_a_i, 0);
#10;
force_1bit_logic(vif.sig_a_i, 1);
end
endmodule

get virtual interface once in package

I try to write a package which include some common tasks and functions for test environment use.
For example,
package test_misc_pkg;
`include "uvm_macros.svh"
import uvm_pkg::*;
task wait_rest();
virtual test_if test_vif;
if (!uvm_config_db #(virtual test_if)::get(null, "uvm_test_top.env", "test_vif", test_vif))
`uvm_fatal("NOVIF", "virtual interface should be set")
#(posedge test_vif.clk);
//do something...
endtask
endpackage : test_misc_pkg
Then, test env can just call wait_rest() after import test_misc_pkg.
But, if there are other task use same test_vif, I still need to get db in the beginning of every tasks.
My question is, can we just get uvm_config_db once in the package, let all tasks can use test_vif directly?
Or, any better way to do this?
Thanks.
You could make you virtual interface a global variable:
package test_misc_pkg;
`include "uvm_macros.svh"
import uvm_pkg::*;
virtual test_if test_vif;
function void set_vif();
if (!uvm_config_db #(virtual test_if)::get(null, "uvm_test_top.env", "test_vif", test_vif))
`uvm_fatal("NOVIF", "virtual interface should be set")
endfunction
task wait_rest();
#(posedge test_vif.clk);
//do something...
endtask
endpackage : test_misc_pkg
and the write a function (eg set_vif()) to give the global virtual interface a value. You could then call this function in the connect_phase, for example. It all seems a little unconventional, though.
If you are trying to reduce the overhead of calling uvm_config_db::get(), you could test to see it is null, and then do the get.
task wait_rest();
virtual test_if test_vif; // this is a static variable
if (test_vif == null) begin
if (!uvm_config_db #(virtual test_if)::get(null, "uvm_test_top.env", "test_vif", test_vif))
`uvm_fatal("NOVIF", "virtual interface should be set")
end
#(posedge test_vif.clk);
//do something...
endtask

Sending bus signal using analysis port

I am using UVM to test very simple interface and now facing with “corner-case” issue.
So I need to send logic[0:7] signal from output monitor to scoreboard.
This is part of the code:
class outputMonitor extends uvm_monitor;
..
logic [7:0] lcdCmd;
uvm_analysis_port #(logic) sendPrt;
task run_phase (uvm_phase phase);
forever
begin
sendPrt.write(lcdCmd) ;
end
endtask
endclass
class scoreboard extends uvm_subscriber #(logic);
`uvm_component_utils(scoreboard)
function void write (logic t);
$display("%t: scoreboard: ########### calling write function-- data=%b", $time, t);
endfunction
endclass
During simulation I see that only the last bit of lcdCmd is transferred to scorebaord. Is there a way to transfer the whole bus data using ports?
Of course I can create struct or transaction, put the lcdCmd there and then send the struct. But why I cannot just send the bus?
Thanks
Hayk
In your code you did a simple mistake.Like...
Pass type argument of analysis fifo as only logic in stand of logic[7:0] vector.
Same thing is applicable for subscriber class parameter and also at write implementation function argument.
See the code which you need to change.
class outputMonitor extends uvm_monitor;
`uvm_component_utils(outputMonitor)
logic [7:0] lcdCmd;
uvm_analysis_port #(logic[7:0]) sendPrt;
task run_phase (uvm_phase phase);
forever
begin
sendPrt.write(lcdCmd);
end
endtask
endclass
class scoreboard extends uvm_subscriber #(logic[7:0]);
`uvm_component_utils(scoreboard)
function void write (logic [7:0] t);
$display("%t: scoreboard: ########### calling write function-- data=%b", $time, t);
endfunction
endclass

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