Getting the hierarchical scope from where a function was called - system-verilog

When calling a function, is it possible for that function to get the hierarchical scope of the location where it was called?
Let me give you an example. I have the following code:
package some_pkg;
function void some_function();
$display("%m");
endfunction
endpackage
module top;
import some_pkg::*;
initial
some_function();
endmodule
When running it, it will display "some_pkg::some_function". Is there any way that I can get the function to display "top"? Or if I would have some other sub-module where it is called from, could it display "top.submodule"?

This is going to be tool specific and requires extra debugger information to dynamically track scopes. Modelsim/Questa has $stacktrace that will display the scope and filename/linenumber

I kind of got it working using a macro:
`timescale 1ns/1ns
`define here_str $sformatf("%s::%0d %m", `__FILE__, `__LINE__)
`define here(DUMMY="") $display("I am here: %s", `here_str);
package some_pkg;
function void some_function();
$display("%m");
endfunction
endpackage : some_pkg
module top;
import some_pkg::*;
initial begin
some_function(); // Gives the wrong scope https://stackoverflow.com/q/24454395/1219634
$stacktrace; // Does not return the stacktrace as a string value
`here() // This works, but I would rather not use a macro as I
// would like to make this portable in a package
$finish;
end
endmodule : top
Tested on Cadence/Xcelium 18.09-s014.

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.

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

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

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

How to pass parameterized class to a module instance?

include "cls.sv"
module top();
int x;
spl_cls #(10) o_spl_cls;
/*Code Continues, here o_spl_cls object has been created using
new();*/
dummy i_dummy(o_spl_cls); //I instantiated another module and passed
//object
endmodule
//This is my dummy module
module dummy(spl_cls i_spl_cls);
....
endmodule
//my spl_cls is
class spl_cls#(int data=0);
//...rest code
endclass
I am getting Error.
How to pass parameterized object to another module instance or class?
You will need to parameterize all references to spl_cls that you want to make assigments to.
module dummy(spl_cls#(10) i_spl_cls);
....
endmodule
A better solution would be to use a typedef
module top();
typedef spl_cls #(10) spl_cls_10;
int x;
spl_cls_10 o_spl_cls;
/*Code Continues, here o_spl_cls object has been created using
new();*/
dummy #(spl_cls_10) i_dummy(o_spl_cls); //I instantiated another module and passed
//object
endmodule
//This is my dummy module
module dummy #(type C)(C i_spl_cls);
....
endmodule

Systemverilog doesn't allow variable declarations after call to super.foo()?

I'm running into a weird issue working with SystemVerilog on DVT. The code snippet in question looks something like this:
class parent;
int A;
function void foo();
A = 5;
endfunction
endclass
class childA extends parent;
function void foo();
bit test_one; //Does not flag as a syntax error.
super.foo();
bit test_two; //Flags as error: Expected endfunction, found bit.
endfunction //Subsequently: Expected endclass, found endfunction
endclass //And lastly: Unexpected token: Endclass
As far as I know it is legal to call any hidden parent function using super. but this behavior is perplexing me. Can someone tell me if this is legal SV syntax? Or if not: What's the reasoning behind this?
It is illegal syntax. All variables in a task or function must be declared before any operation. See IEEE Std 1800-2012 ยง 13 Tasks and functions (subroutines)
Legal syntax is:
function void foo();
bit test_one;
bit test_two;
super.foo();
endfunction
The only exception is a begin-end block in which case the variable can be declared at the top of the begin-end block before any operation (but you can nest begin-end block). This does however limit scope access and may be less readable. So it not a good practice
function void foo();
bit test_one;
super.foo();
begin
bit test_two; // only in scope within this begin-end
begin
bit test_three; // not the same 'test_three' as below, different scope
end
begin
bit test_three; // not the same 'test_three' as above, different scope
end
// both 'test_three's are out of scope
end
// 'test_two' both 'test_three's are out of scope
endfunction
General best practice is to always declare your variables at the top. I prefer adding empty space between variable declarations and operations a visual separator; makes reading and modifying a ascetically easier.
function void foo();
bit test_one;
bit test_two;
super.foo();
endfunction