Automatic variables inside static method

If I understand correctly, if I declare a class method as static, also all the “local” variables inside that method become static?

E.g.

class X;
     static void function f();
         int a;
         ...
     endfunction
endclass

So if several threads would call X.f() concurrently they would be influencing eachothers shared ‘a’ variable, correct?

Is the only solution declaring all the local variables as automatic?

Not correct.

The keyword ‘static’ is overloaded. There is a semantic difference between the meaning of ‘static’ used to the left of a function/task keyword and ‘static’ used to the right of the function/task keyword.

When used to the left of the function or task, ‘static’ is a class qualifier and has the same as on any static method in C++/Java. It is a method of the class type, not of an instance of a class object. Other class qualifiers are ‘local’ and ‘protected’ and along with ‘static’ are only allowed in front of methods of a class. There is no corresponding ‘automatic’ class qualifier.

When used to the right of a function, ‘static’ is a lifetime qualifier, with ‘automatic’ being the corresponding qualifier. The lifetime of a function or task is a Verilog-only concept. Verilog started out with having only static lifetimes of functions or tasks, meaning that there was no call stack for arguments or variables local to the routines. This meant you could not have recursive or re-entrant routines, unlike most other modern programming languages. Verilog 2001 added the ‘automatic’ lifetime qualifier to give routines the normal behavior of most programming languages. SystemVerilog added a lifetime qualifier for modules so that all routines defined in that module would be considered automatic by default so you didn’t have to add the automatic keyword after each function or task declaration. SV also added the ‘static’ lifetime qualifier so that if for some stupid reason you declared a module as ‘automatic’ but still needed a particular function inside that module to have the original Verilog behavior.

Because of backward compatibility reason, SystemVerilog could not change the default lifetime qualifier to ‘automatic’ but since class methods were new, it changed the lifetime of all methods to be automatic. In later revision of SV, you are no longer even allowed to specify a ‘static’ lifetime for a class method. This also clears up any confusion in seeing

static function // only legal as a class method qualifier

versus

function static // only legal as a lifetime qualifier of a non-class method.

I never realized that static had these 2 overloaded meanings.

So as long as you work in a class based environment, everything has always automatic lifetime, and the static keyword can only be to the left of the function and has the same meaning as in C++, correct? I.e. everything behaves like in C++.

Thanks for the excellent explanation!

In reply to Nico75:

Hi Dave,

I am kind of totally getting lost/confused with the usage of static keyword at the left of a function/task. Yes I am clear with the fact that if we declare say a static var int a; or function static (); It creates a single location in stack memory and, all the objects share that same mem location and, the update by 1 object of the var int a will be seen across by all the objects.

But when it comes to Static on the LHS of a function/Task I am still not very clear.This is what I know of that “Static written at left implies to lifetime of method with respect to class and it is associated with class type, not with object. It is a method of class type, not of an instance of a class object”.

But as you would know this static method can be accessed by anyone outside without creating a class object using the scope resolution operator. So say we declare 2 class objects of class X and, we try to access the method (function f). set the var int a to some value using 1st object and, to another value using 2nd object. Now what will the value be returned when we try to access it using scope resolution X::f(). Can you please kindly explain what may happen here ?

I have seen your System verilog OOP course for UVM it was very good :) I have a question on why we use the static function create method when trying to create the actual object from the factory when using the proxy class. My understanding regarding that is since we can directly call that method using scope resolution we use that and, so even if user creates wishes to create multiple objects for (accessing the create() method) it all will refer to the same single instance that can exist even without creating an actual instance. This helps in creation of the Singleton. My understanding might be flawed to an extent,
please kindly correct me if I am wrong.

Thanks.

In reply to sriram.seshagiri:

Hi Dave,

I tried this example and, I think I got some better clarity now. So Looks like if you try to access a non static variable inside a static method (static function (static keyword on LHS) of a class type), these variables are not shared among different instances (objects). They are independent of each other and, even when I tried to access using scope resolution it evaluated it independently. But the static variable did get shared by all instances of the class.
Below is the example:__

class base;

static function set (int k);
int j;
static int i;

i++;

$display("Before init value of j");
$display(j);

j=k;
//i = i+j;
$display("value of i"); //i is static var
$display(i);
$display("After init value of j");
$display(j);

endfunction

static function get;

//$display(i);

endfunction

endclass

module automatic test();
initial begin
base b1,b2;
b1 = new();
b2 = new();
repeat(10) begin
b1.set(5);
end
$display(“@@@@”);
b2.set(5);
base::set(0);

Output:

Before init value of j
0
value of i
1
After init value of j
5
Before init value of j
0
value of i
2
After init value of j
5
Before init value of j
0
value of i
3
After init value of j
5
Before init value of j
0
value of i
4
After init value of j
5
Before init value of j
0
value of i
5
After init value of j
5
Before init value of j
0
value of i
6
After init value of j
5
Before init value of j
0
value of i
7
After init value of j
5
Before init value of j
0
value of i
8
After init value of j
5
Before init value of j
0
value of i
9
After init value of j
5
Before init value of j
0
value of i
10
After init value of j
5
@@@@
Before init value of j
0
value of i
11
After init value of j
5
Before init value of j
0
value of i
12
After init value of j
0

In reply to sriram.seshagiri:

So I think the reason UVM object Registry class uses a static function (static keyword on LHS) for create() method is because it can easily access the local static variable me and, also we don’t have to create an instance for calling the function get (), can be done by scope resolution. Though no user has to do that here since like you said when the user does:
type_Id:: create();
It becomes typedef objectRegistry#(C) typeId; so this calls the static method create inside this specialized class named typeID. So just by referencing this typedef the static properties in the ObjectRegistry class gets allocated and, initialized.

Please correct me if I am wrong. Thanks.