Professional Documents
Culture Documents
In SystemVerilog IEEE 1800-2012 LRM (Chapter 8.10 page 141), a static method is defined as:
A static method is subject to all the class scoping and access rules, but behaves like a regular subroutine
that can be called outside the class, even with no class instantiation
whereas automatic is seen as (Chapter 6.21 page 90):
Variables declared in an automatic task, function, or block are local in scope, default to the lifetime of
the call or block, and are initialized on each entry to the call or block.
Simple example
Well use the following example to illustrate some static related aspects:
class my_class;
function int foo(int a=1);
foo = 3;
endfunction
static function int foo_static_1(int a=1); // the method is static, argument
and internal variables are automatic
foo_static_1 = 3;
endfunction
static function static int foo_static_2(int a=1); // the method is static,
argument and internal variables are static
int b;
foo_static_2 = 3;
endfunction
endclass
Twice static?
You might have already asked yourself, what is the difference between foo_static_1 and foo_static_2. The only
difference is that foo_static_2 has an extra static keyword interpolated between the function keyword and the int return
type. What does that mean? The second static keyword from foo_static_2 says that both the arguments of the method
and the variables inside the method are implicitly declared as static. Lets see this in action:
module top;
initial begin
my_class::foo_static_2.a = 10;// a is the argument of the method
my_class::foo_static_2.b = 20;// b is a field inside the method
$display("my_class::foo_static_2.a=%0d",my_class::foo_static_2.a);
$display("my_class::foo_static_2.b=%0d",my_class::foo_static_2.b);
end
endmodule
OUTPUT:
my_class::foo_static_2.a=10
my_class::foo_static_2.b=20
Using class scope operator, you can actually alter the value of the methods argument. Moreover, thats done from
outside the method, without actually calling it. This is not a typical behavior than one might expect.
Each function has an implicit variable, named after the functions name. The value of that implicit variable will be
returned at the end of the function. Now, with the second static in place, we can also access this implicit variable. Its
interesting to notice that the implicit variable is set to the default value, 0, if the function has not been called yet. But,
after calling the function, the implicit variable has changed to the value computed by the function. Check it out below:
module top;
initial begin
$display("my_class::foo_static_2.foo_static_2",
my_class::foo_static_2.foo_static_2);
$display("my_class::foo_static_2()", my_class::foo_static_2());
$display("my_class::foo_static_2.foo_static_2",
my_class::foo_static_2.foo_static_2);
end
endmodule
OUTPUT:
my_class::foo_static_2.foo_static_2
my_class::foo_static_2()
my_class::foo_static_2.foo_static_2
3
3
At time 0 value of a is 1
value of b is 0
At time 2 value of a is 100 value of b is 200
Conclusions
A static function is useful when you need global functions encapsulated under a common scope (usually a class); e.g.
mathematical functions, string processing functions, etc. Having a static task seems to be less useful compared to
functions. An use case in which Ive used a static task is to implement a wait task that waits as many simulation cycles
as mentioned via the tasks argument. I consider these type of methods to be a kind of an utility library, stored within
one utility class.
Having static function static or static task static is not that intuitive since the SystemVerilog IEEE 1800-2012 LRM
doesnt mention anything about it. Even if this distinction, between static function and static function static, is absent
from the LRM, I find it important. What do you think? Do you have any scenarios where using static function static, is
helpful? Share your thoughts on this.
cg_wrapper cgs[5];
foreach (cgs[max_size]) begin
cgs[max_size] = new(max_size + 1);
for (int size = 1; size <= max_size + 1; size++)
cgs[max_size].size_cg.sample(size);
end
end
endmodule
}
max_burst_size_three_or_more: coverpoint size iff (max >= 3) {
option.weight = (max >= 3);
bins one_to_one
= (1
=> 1);
bins one_to_several
= (1
=> [2:max-1]);
bins one_to_max
= (1
=> max);
bins several_to_one
= ([2:max-1] => 1);
bins several_to_several = ([2:max-1] => [2:max-1]);
bins several_to_max
= ([2:max-1] => max);
bins max_to_one
= (max
=> 1);
bins max_to_several
= (max
=> [2:max-1]);
bins max_to_max
= (max
=> max);
}
endgroup
function new(int max_size);
size_cg = new(max_size);
size_cg.set_inst_name($sformatf("size_cg_max_size_%0d", max_size));
size_transitions_cg = new(max_size);
size_transitions_cg.set_inst_name($sformatf("size_transitions_cg_max_size_
%0d", max_size));
endfunction
endclass
module test;
initial begin
int size;
int max_size;
cg_wrapper cgs[5];
foreach (cgs[i]) begin
max_size = i + 1;
cgs[i] = new(max_size);
repeat (1000) begin
void'(std::randomize(size) with { size inside { [1:max_size] }; });
cgs[i].size_cg.sample(size);
cgs[i].size_transitions_cg.sample(size);
end
end
end
endmodule
CALL, RET,
LOAD, STORE,
INT,
NOP
} opcode_t;
//
//
//
//
Function calls
Memory access
Interrupt
Miscellaneous
//
//
//
//
//
//
//
//
Arithmetic
Logic
Jumps
Function calls
Memory access
Interrupt
Miscellaneous
Vector arithmetic
end
end
endmodule