passing different type of argument to method in systemverilog - system-verilog

Trying to understand the concept of casting.
class base;
local string a;
function new();
a = "I am a";
endfunction
function void print();
$display(a);
endfunction
endclass
class ext extends base;
local string b;
function new();
b = "i am b";
endfunction
function void print();
$display(b);
endfunction
endclass
function void printer(base p);
ext e;
$cast(e, p);
e.print();
p.print();
endfunction
program P;
base b = new();
ext e = new();
initial begin
printer(b);
end
endprogram
After printer(b) executed then I get cast and Null pointer Error as the below.
I thought that printer(b) send base type so there is no casting to printer(base p) as a base argument. then $cast(e, p); down-casts from base to ext. Why does this casting invalid?
xcelium> run
$cast(e, p);
|
xmsim: *E,BCLCST (./testbench.sv,24|6): Invalid cast: a value with the class datatype '$unit_0x4ccdf83b::base' cannot be assigned to a class variable with the datatype '$unit_0x4ccdf83b::ext'.
xmsim: *E,TRNULLID: NULL pointer dereference.
File: ./testbench.sv, line = 17, pos = 13
Scope: worklib.$unit_0x4ccdf83b::ext::print
Time: 0 FS + 1
Verilog Stack Trace:
0: function worklib.$unit_0x4ccdf83b::ext::print at ./testbench.sv:17
1: function worklib.$unit_0x4ccdf83b::printer at ./testbench.sv:26
2: initial block in P at ./testbench.sv:36
./testbench.sv:17 $display(b);
xcelium> exit
If I ran printer(b); after printer(e); then there is no null pointer error but still Invalid case.
Why does casting invalid happen and Null point error?

In your program, you create two separate objects:
a base object containing a member a and a method base::print.
an extended object containing members a and b, and methods base::print and ext::print.
You are never allowed to make an assignment from a base object to to an extended class variable e.
Lets assume you did not declare b as a local variable. If SystemVerilog did allow assignments from base object to extended, and you tried to reference e.b, the member does not exist.
You are allowed to make assignments in the other direction--from extended object to base class variable. That is what happens when you call printer(e)
You need to test the result from $cast. It returns 0 if the cast fails to make the assent to e leaving it null.
function void printer(base p);
ext e;
if ($cast(e, p))
e.print();
p.print();
endfunction
module P;
base b = new();
ext e = new();
initial begin
printer(b);
printer(e);
end
endmodule
Note that this prints 3 lines (1 from printer(b) and 2 from printer(e))
# I am a
# i am b
# I am a

Related

Creating an array of child handles

In SystemVerilog, I have a base class A and derived class B, C, D. I would like to create an array of type A which has handles to B, C, D. Is there a more succinct way to do this than my ugly solution below?
module test;
A arr[3];
B b;
C c;
D d;
initial begin
b = new();
c = new();
d = new();
arr[0] = b;
arr[1] = c;
arr[2] = d;
end
endmodule
If you mean without using the intermediate class variables, you can do
arr[0] = B::new();
This is a relatively new feature of SystemVerilog and some tools do not support this yet. To get around this, you can create a static create method that calls the constructor for you, which lets you create a class object in places like an argument to a function without ever having to declare an intermediate variable.
class B;
static function C create;
create = new;
endfunction
endclass
arr[1] = C::create();
somefunction(C::create()); // C::new() would not work here
If you are using the UVM library, you get this for free
class C extends uvm_object;
`uvm_object_utils(C)
...
endclass
arr[3] = C::type_id::create();
You can do that array assignment in a single statement.
initial begin
b = new();
c = new();
d = new();
arr = '{b, c, d};
end

Randomize a queue of objects in systemverilog

I have seen techniques used to randomize queues of integral values, but I am having difficulties with queues of objects. An example code:
class int_queue_class;
rand int x[$];
function void print;
$display($sformatf("Size: %0d", x.size()));
foreach(x[i])
$display($sformatf("x[%0d] = %0d", i, x[i]));
endfunction : print
endclass : int_queue_class
class obj;
rand int value;
endclass : obj
class obj_queue_class;
rand obj x[$];
function void print;
$display($sformatf("Size: %0d", x.size()));
foreach(x[i])
$display($sformatf("x[%0d] = %0d", i, x[i].value));
endfunction : print
endclass : obj_queue_class
program top();
initial
begin
int_queue_class iqc;
obj_queue_class oqc;
iqc = new();
iqc.randomize() with {x.size() == 5; x[2] == 5;};
iqc.print();
oqc = new();
oqc.randomize() with {x.size() == 5; x[2].value == 5;};
oqc.print();
end
endprogram : top
It seems that the same approach cannot be used when it comes to objects. Is there a way to randomize such a queue while keeping the ability to simultaneously constrain the queue size and individual element fields? The ideas that I've come up with that use pre/post randomize methods all loose at least one of those two options.
Randomization doesn't create new objects. When you randomize the size of a queue it will create new entries which contain the default values, which for class types is null.
The only thing you can do is to pre-allocate objects inside your queue, to have some to spare. You won't be able to randomize more objects than you initially created, so this limits the value of the queue's size:
class obj_queue_class;
rand obj x[$];
local const max_size;
constraint legal_size {
x.size() <= max_size;
}
function void pre_randomize();
x = new[max_size];
foreach (x[i])
x[i] = new();
endfunction
function void print();
$display($sformatf("Size: %0d", x.size()));
foreach(x[i])
$display($sformatf("x[%0d] = %0d", i, x[i].value));
endfunction
endclass
I've made max_size a local constant value, but it can be something that you can set from outside, a package parameter, etc.
In pre_randomize() you instantiate max_size objects. Since x.size() is always smaller or equal to max_size, whatever value gets randomized will lead to a queue that contains object instances and no null. The rest of the objects will get thrown away when the queue shrinks.

output *E,TRNULLID: NULL pointer dereference. System verilog

class tx;
pkt p;
int j;
function new ( pkt p);
p = new();
j =10;
endfunction
task copy(pkt p);
this.p = new p;
endtask
endclass :tx
initial
begin
tx t1,t2;
pkt p;
t1 =new();
p = new();
p.i=256;
t2= new t1;
t2.j=20;
t2.copy(p);
$display(t1.j);
$display(t2.j);
$display(p.i);
$display(t1.p.i);
$display(t2.p.i);
t1.p.i=221;
$display(t1.p.i);
$display(t2.p.i);
end
endprogram
Why this code is not giving output. when i change t1 = new (p). it works fine
but give error for few lines
ncsim> run 10 20 256 ncsim: *E,TRNULLID: NULL pointer dereference.
While it doesn't print for
$display(t1.p.i);
$display(t2.p.i);
Null pointer errors occurs when trying to access a object that doesn't exist.
For the case of t1 = new(), there should be an warning in the compile/elaboration log. Something along the lines of missing input p. Changing t1 = new() to t1 = new(p) may appear to change resolve the error on that line, but t1.p is still null. This is because input variable has the same name of the as a member variable. When you newed the p inside tx's new it used the input variable because it was closer in scope. Since you assigned a new object to a input handle, the original handle is not pointing to the same object. It would be pointing the the same object if you used you defined the directionality as inout or ref instead as an inferred input. Still the member p would be null.
Solutions:
Change tx's function new ( pkt p); to function new ();. The input p doesn't appear to be doing anything so there is no reason to have it. p = new() will be assigned to tx's member variable since there is no name conflict.
Change p = new() to this.p = new() in tx's new method. This makes it explicit that the new will apply to the member variable, not the local.
Do both solutions 1 and 2.
If you need both ps and you do not want to do the above, then rename one and use accordingly.

Understanding function return values

I am trying to understand SystemVerilog function return values from the Language resource manual(Section 10.3.1), but I am having difficulties in grasping the following section. Can anyone help me interpret it? I tried looking in different sites but the information wasn't that deep.
In SystemVerilog, a function return can be a structure or union. In this case, a hierarchical name used inside the function and beginning with the function name is interpreted as a member of the return value. If the function name is used outside the function, the name indicates the scope of the whole function. If the function name is used within a hierarchical name, it also indicates the scope of the whole function.a = b + myfunc1(c, d); //call myfunc1 (defined above) as an expression
myprint(a); //call myprint (defined below) as a statement
function void myprint (int a);
...
endfunction
You can use two different ways to return a value from a function. For example as follows,
function int myfunc1(int c, d);
myfunc1 = c+d;
endfunction
and
function int myfunc1(int c, d);
return c+d;
endfunction
So when the function is declared as a structure or union type, the hierarchical name beginning with the function name also means the variable of the return value.
But the old LRM description is not right and precise now because the hierarchical name now could also be the function scope, not the return value. For example,
typedef struct { int c, d, n; } ST;
function ST myfunc1(int c, d);
static int x = 1;
myfunc1.c = c; // myfunc1 refers to the return structure
myfunc1.d = d; // myfunc1 refers to the return structure
myfunc1.n = c + d + // myfunc1 refers to the return structure
myfunc1.x; // myfunc1 refers to function scope
endfunction
Another interesting example of using hierarchical name containing the function name.
typedef struct { int c, d; } ST ;
module top;
function ST myfunc1(int c,d);
top.myfunc1.c = c;
myfunc1.c = 1;
myfunc1.d = d;
endfunction
ST x;
initial begin
x = myfunc1(3,2);
#1 $display("%d %d %d", x.c, x.d, top.myfunc1.c);
end
endmodule
The function call of x = myfunc1(3,2) constructs a call frame for myfunc1 and pass values for evaluation. The scopes of myfunc1 and top.myfunc1 are different. The hierarchical name beginning with myfunc1 refers to current call frame of the function, while top.myfunc1 refers to the scope of function declared inside the module top . So the message will be 1 2 3.
It looks like you are referencing a really old version of the LRM. Get the latest official version IEEE Std 1800-2012. You'll want to look at § 13.4.1 Return values and void functions. There is a line missing between quoted paragraph and quoted code:
Functions can be declared as type void, which do not have a return
value. Function calls may be used as expressions unless of type void,
which are statements:
The example code is not referring you your question hierarchical name access, it is an example of the void return type.
The example code below demonstrates hierarchical name access with a struct/union return types. Read about structs and unions in § 7.2 and § 7.3.
function struct { byte a,b; } struct_func (byte a,b);
byte c;
c = a ^ b;
struct_func.a = a; // <== hierarchical name used inside the function
struct_func.b = ~b;
endfunction
initial begin
// function name is used within a hierarchical name ( struct member )
$display("%h", struct_func(8'h42,8'hFE).b ); // returns 01
// function name is used within a hierarchical name ( function member )
$display("%h", struct_func.b ); // returns fe (last value of input b)
// function name is used within a hierarchical name ( function member )
$display("%h", struct_func.c ); // returns bc (last value of variable c)
end
Most cases you want to reuse struct/union definitions and should be defined as a typedef. The below function with the yeild the same results with the above initial block.
typedef struct { byte a,b; } my_struct;
function my_struct struct_func (byte a,b);
byte c;
c = a ^ b;
struct_func.a = a; // <== hierarchical name used inside the function
struct_func.b = ~b;
endfunction

SystemVerilog passing functions as an argument

Is it possible to pass a function as an argument in SystemVerilog?
This code hopefully demonstrates though it doesn't work. Any help? Thanks.
module funcparam;
int result;
function int xxx(int x, ref fun);
return fun(x);
endfunction
function int yyy(int y);
return y * (y + y);
endfunction
initial begin
result = xxx(5, yyy);
$display("result: %d", result);
end
endmodule
You're limited as to what can be passed by reference:
a variable,
a class property,
a member of an unpacked structure, or
an element of an unpacked array.
You might be able to pass in a handle of a base class, though I doubt this would work.
class base;
function yyy(int x);
endfunction
endclass
class class1 extends base;
function yyy(int x);
endfunction
endclass
class class2 extends base;
function yyy(int x);
endfunction
endclass
module funcparam;
result;
function int xxx(int x,input base fun);
return fun.yyy(x);
endfunction
class1 cls = new;
//class2 cls = new;
initial begin
result = xxx(5, cls);
$display("result: %d", result);
end
endmodule
No.
Tasks and functions can only accept data types as arguments, and functions are not data types. Also there is no way to make a function into a data type.