You are on page 1of 9

What do static and its automatic counterpart mean?

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

Never call a static method using an object


Since the foo() method is non-static, we need to create an object, named my_object, in order to call it. Similarly,
my_object can be used to call the other two methods, foo_static_1() and foo_static_2(), but this is NOT recommended
since static fields/methods dont belong to objects. For static methods it is highly recommended to use the class scope
resolution operator (see next chapter).
module top;
my_class my_object;
initial begin
my_object = new;
$display("Calling methods via object");
$display("my_object.foo()=%0d",my_object.foo());
$display("my_object.foo_static_1()=%0d", my_object.foo_static_1());//not
recommended, even though it's a legal syntax
$display("my_object.foo_static_2()=%0d", my_object.foo_static_2());//not
recommended, even though it's a legal syntax
end
endmodule
OUTPUT:
Calling methods via object
my_object.foo()=
3
my_object.foo_static_1()=3
my_object.foo_static_2()=3

Calling a static method using the class scope resolution operator ::


Even if no object of type my_class is created, we can call the static methods by using class scope resolution operator ::
like this:
module top;
initial begin
$display("Calling methods via class scope resolution operator");
$display("my_class::foo_static_1()=%0d",my_class::foo_static_1());
$display("my_class::foo_static_2()=%0d",my_class::foo_static_2());
$display("my_class::foo_static_2.foo_static_2=
%0d",my_class::foo_static_2.foo_static_2);//foo_static_2 is the implicit return
variable of the function foo_static_2
end
endmodule
OUTPUT:
Calling methods via class scope resolution operator
my_class::foo_static_1()= 3
my_class::foo_static_2()= 3
my_class::foo_static_2.foo_static_2= 3

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

What about tasks?


Tasks, usually, are time consuming methods, When they dont consume time, they behave as functions that dont return
values. If simulation time is being consumed by the task as seen below, interesting scenarios may pop up. For example
foo_static_task starts with a set of values for the argument a and variable b. The task itself does not change the values of
a and b, but after a simulation delay #2 their values are not the same anymore. Argument a and variable b were changed
from outside the body of the task. This is because both the method (foo_static_task) and the variables (a,b) are static:
class my_class;
static task static foo_static_task(int a=1); // the method is static, argument
and internal variables are static
int b;
$display("START TASK. At time %0d value of a is %0d value of b is %0d",
$time, a, b);
#2;
$display("END TASK. At time %0d value of a is %0d value of b is %0d",$time,
a, b);
endtask
endclass
module top;
initial begin
fork
my_class::foo_static_task();
join_none
#1
my_class::foo_static_task.a = 100;
my_class::foo_static_task.b = 200;
end
endmodule
OUTPUT:
START TASK.
END TASK.

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.

How to Implement Flexible Coverage


Definitions (Part 1)
Lets consider the case where you need to cover the size of a burst, which can range between 1..max, with max being
configurable within 1..N. The bins are defined as 1, 2..max-1 and max.

The first implementation that comes to mind is the following:


covergroup size_cg(int max) with function sample(int size);
option.per_instance = 1;
type_option.merge_instances = 1;
burst_size: coverpoint size {
bins one_item
= { 1 };
bins several_items = { [2:max-1] };
bins max_items
= { max };
illegal_bins illegal_val = default;
}
endgroup
Even though it captures our intention, this covergroup definition is faulty. If max = 2, the several_items bin will be
[2:1], which overlaps with the other two bins. When max = 1, the max_items bin will be { 1 }, which overlaps with the
one_item bin. The several_items bin will be [2:0]. Not only does it overlap with the one_item and max_items bins, but it
also accepts the illegal values 0 and 2, which is clearly a problem.
Solution Using the with clause
To solve this issue, the with clause will be used for the several_items and max_items bins.
covergroup size_cg(int max) with function sample(int size);
option.per_instance = 1;
type_option.merge_instances = 1;
burst_size: coverpoint size {
bins one_item
= { 1 };
bins several_items = { [2:max-1] } with (max >= 3);
bins max_items
= { max }
with (max >= 2);
illegal_bins illegal_val = default;
}
endgroup
As a consequence, the max_items bins will be created only if max >= 2, while the several_items bins will be created
only if max >= 3.
The Test Module
If you want to play with the presented solution, you can use the test module below:
class cg_wrapper;
covergroup size_cg(int max) with function sample(int size);
option.per_instance = 1;
type_option.merge_instances = 1;
burst_size: coverpoint size {
bins one_item
= { 1 };
bins several_items = { [2:max-1] } with (max >= 3);
bins max_items
= { max }
with (max >= 2);
illegal_bins illegal_val = default;
}
endgroup
function new(int max_size);
size_cg = new(max_size);
size_cg.set_inst_name($sformatf("size_cg_max_size_%0d", max_size));
endfunction
endclass
module test;
initial begin

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

How to Implement Flexible Coverage


Definitions (Part 2)
In part 1 of this 3-post series, I presented a way of defining flexible coverage definitions in SystemVerilog using the
with clause.
In this second post, I will show a way of achieving the same flexibility for transition coverage. As SystemVerilogs
grammar doesnt allow us to use the with clause for defining transition bins, we will be using the coverpoint coverage
option called weight.
The example given in part 1 will be enhanced with transitions for the burst size from any of the three bins to any of the
three bins, for a total of 9 possible transitions.
Solution using option.weight
Although more commonly used for defining cross-coverage, option.weight gives us a way to exclude coverpoints from
the computation of the total coverage score. This feature can be used to our advantage.
Lets define three coverpoints: one for max = 1, one for max = 2 and one for max >= 3. Depending on the value of max,
only one of the three coverpoints will have the weight equal to 1. The weight of the other two coverpoints will be 0, so
they will be excluded from the coverage computation. The covergroups total coverage grade will be equal to the
coverage grade of the coverpoint which has the weight equal to 1.
I provide you a working example below:
class cg_wrapper;
covergroup size_cg(int max) with function sample(int size);
option.per_instance = 1;
type_option.merge_instances = 1;
burst_size: coverpoint size {
bins one_item
= { 1 };
bins several_items = { [2:max-1] } with (max >= 3);
bins max_items
= { max }
with (max >= 2);
illegal_bins illegal_val = default;
}
endgroup
covergroup size_transitions_cg(int max) with function sample(int size);
option.per_instance = 1;
type_option.merge_instances = 1;
max_burst_size_one: coverpoint size iff (max == 1) {
option.weight = (max == 1);
bins one_to_one = (1 => 1);
}
max_burst_size_two: coverpoint size iff (max == 2) {
option.weight = (max == 2);
bins all_to_all[] = (1, 2 => 1, 2);

}
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

How to Implement Flexible Coverage


Definitions (Part 3)
In the final part of this 3-post series (Part 1, Part 2), I will show a way of covering enum transitions and conditionally
ignoring transitions to and from certain enum values.
For example, in the case of a CPUs instruction set, you want to make sure that all possible combinations of two
consecutive instructions are executed. The most straightforward implementation is to define an enum with all the
opcodes, and a transition bin for the opcode:
class instruction;
typedef enum {
ADD, SUB, MUL, DIV,
// Arithmetic
AND, OR, XOR, NOT,
// Logic
JMP, JZ, JNZ, JLT, JGT, // Jumps

CALL, RET,
LOAD, STORE,
INT,
NOP
} opcode_t;

//
//
//
//

Function calls
Memory access
Interrupt
Miscellaneous

rand opcode_t opcode;


covergroup instruction_cg();
option.per_instance = 1;
opcode_transitions: coverpoint opcode {
bins opcode_transitions[] = (
ADD, SUB, MUL, DIV, AND, OR, XOR, NOT, JMP, JZ, JNZ, JLT, JGT, CALL,
RET, LOAD, STORE, INT, NOP
=>
ADD, SUB, MUL, DIV, AND, OR, XOR, NOT, JMP, JZ, JNZ, JLT, JGT, CALL,
RET, LOAD, STORE, INT, NOP
);
}
endgroup
...
endclass
This code accomplishes what we wanted, but what happens if the requirements change? Lets suppose that several CPU
versions must be created. Some of them wont support the hardware DIV instruction, while others should include the
vector arithmetic instructions VADD, VSUB, VMUL, and VDIV.
The way we defined the transition coverage doesnt allow us to easily implement these changes. In addition, when
instructions are added or removed, we have to update the definitions for both the enum and the transition bin. This
process can become tedious and error prone as the number of instructions increases.
One possible solution is to add an opcode named INVALID in the enum definition and ignore all transitions to and from
it:
class instruction;
typedef enum {
INVALID = -1,
...
} opcode_t;
rand opcode_t opcode;
covergroup instruction_cg();
option.per_instance = 1;
opcode_transitions: coverpoint opcode {
bins opcode_transitions[] = ([INVALID:$] => [INVALID:$]);
ignore_bins ignore_invalid = (INVALID => [INVALID:$]), ([INVALID:$] =>
INVALID);
}
endgroup
...
endclass
To exclude the DIV instruction from the transition coverage, we can use the conditional operator:
covergroup instruction_cg(bit disable_div);
option.per_instance = 1;
opcode_transitions: coverpoint opcode {
bins opcode_transitions[] = ([INVALID:$] => [INVALID:$]);
ignore_bins ignore_div =

(disable_div ? DIV : INVALID => [INVALID:$]),


([INVALID:$] => disable_div ? DIV : INVALID);
ignore_bins ignore_invalid = (INVALID => [INVALID:$]), ([INVALID:$] =>
INVALID);
}
endgroup
If disable_div is equal to 1, all transitions to and from DIV are ignored. Otherwise, the ignore_div has the same
definition as the ignore_invalid bin. The same technique can be used to conditionally exclude the vector instruction
from the coverage.
You can find a complete example below:
class instruction;
typedef enum {
INVALID = -1,
ADD, SUB, MUL, DIV,
AND, OR, XOR, NOT,
JMP, JZ, JNZ, JLT, JGT,
CALL, RET,
LOAD, STORE,
INT,
NOP,
VADD, VSUB, VMUL, VDIV
} opcode_t;

//
//
//
//
//
//
//
//

Arithmetic
Logic
Jumps
Function calls
Memory access
Interrupt
Miscellaneous
Vector arithmetic

rand opcode_t opcode;


covergroup instruction_cg(bit disable_div, bit disable_vector);
option.per_instance = 1;
opcode_transitions: coverpoint opcode {
bins opcode_transitions[] = ([INVALID:$] => [INVALID:$]);
ignore_bins ignore_div =
(disable_div ? DIV : INVALID => [INVALID:$]),
([INVALID:$] => disable_div ? DIV : INVALID);
ignore_bins ignore_vector =
([(disable_vector ? VADD : INVALID) : (disable_vector ? VDIV : INVALID)]
=> [INVALID:$]),
([INVALID:$] => [(disable_vector ? VADD : INVALID) : (disable_vector ?
VDIV : INVALID)]);
ignore_bins ignore_invalid = (INVALID => [INVALID:$]), ([INVALID:$] =>
INVALID);
}
endgroup
function new();
instruction_cg = new(.disable_div(0), .disable_vector(1));
instruction_cg.set_inst_name("instruction_cg");
endfunction
endclass
module test;
initial begin
static instruction instr = new;
repeat (10000) begin
void'(instr.randomize() with { opcode != INVALID; });
instr.instruction_cg.sample();

end
end
endmodule

You might also like