When should be program block dynamic or static ? members of static program block , members of automatic program blocks?

HI ,

by default the program block is static , the members also static in nature.

suppose i declare any object_handle inside program block, is it memory allocation (object creation ) done for it at compilation time ? if it is not done , the object_handle is static or dynamic?

  1. please tell me what about default property of program block ?

The program block calls only one time in Verification Environment, so i thinking, i do not require to declare program block as automatic.

  1. please tell me when should can i declare program block as automatic ?

thanks
kbkdec15

Class objects are always dynamically constructed. Class objects are constructed by the new() method and exist as long as at least one class variable has a handle to it. Class objects do not have storage lifetimes associated with a scope, but class variables that holds the handles may.

Automatic storage lifetimes are associated with entering and exiting procedural blocks such as functions, tasks, and named blocks. The default for variables declared in a procedural block outside of a class is to have a static lifetime. That also means the arguments and function return variable are also have static lifetimes by default.

Procedural blocks inside of classes (methods) can only have automatic lifetimes. This matches C/C++ semantics. Static methods, on the other hand, just mean the method is global to the class type and not specific to a particular class object; it has nothing to do with the storage lifetime of the variables declared inside that method.

Because of legacy Verilog which originally had no concept of automatic lifetimes, SystemVerilog had to keep the default lifetime static, but added an option to make all procedural blocks declared within a module/program/interface automatic. So when you declare a program as automatic, it only effects the defaults for variables declared within procedural blocks. Variables declared outside of a procedural block as well as outside a class always have a static lifetime.

By the way, I do not recommend using program blocks - just use modules. See http://go.mentor.com/programblocks

In reply to dave_59:

Hi dave , thank you. it is more helpful for me.

In reply to dave_59:

Class objects are always dynamically constructed. Class objects are constructed by the new() method and exist as long as at least one class variable has a handle to it. Class objects do not have storage lifetimes associated with a scope, but class variables that holds the handles may.
Automatic storage lifetimes are associated with entering and exiting procedural blocks such as functions, tasks, and named blocks. The default for variables declared in a procedural block outside of a class is to have a static lifetime. That also means the arguments and function return variable are also have static lifetimes by default.
Procedural blocks inside of classes (methods) can only have automatic lifetimes. This matches C/C++ semantics. Static methods, on the other hand, just mean the method is global to the class type and not specific to a particular class object; it has nothing to do with the storage lifetime of the variables declared inside that method.
Because of legacy Verilog which originally had no concept of automatic lifetimes, SystemVerilog had to keep the default lifetime static, but added an option to make all procedural blocks declared within a module/program/interface automatic. So when you declare a program as automatic, it only effects the defaults for variables declared within procedural blocks. Variables declared outside of a procedural block as well as outside a class always have a static lifetime.
By the way, I do not recommend using program blocks - just use modules. See http://go.mentor.com/programblocks

Hi Dave,

I have a question and, some confusion here. Since you say that within a class all the methods(task/function) have automatic scope. Below is a code from one of the sequences and, this code snippet is within the task body() method.

class dummy

task body()

foreach (pm.cfg.sel_pid[idx]) begin
curr_pid = pm.cfg.sel_pid[idx];
exp_num_events = 1 + 1 + count[curr_pid] + num_recvq_entries + num_recvq_entries;

for(i=0; i<100; i++) begin
//Monitor HIFIS for the event write
fork
automatic longint eq_pa_r = eq_pa;
hiarb_hifis_req_chk(eq_pa_r,fxr_defs_pkg::Write,num_events);
join_none;

eq_pa = eq_pa + 'h40;
`ovm_info(get_type_name(), $sformatf("New EQ PA : %0x\n", eq_pa), OVM_NONE);
end
endtask
endclass

My Question here is then why I had to make the variable Automatic here since it was within the class. I ran into the problem where all the threads were sharing the same address. But this variable should have been default automatic right since it was in a method within the Class.

Please kindly clarify on this.

In reply to sriram.seshagiri:

You should not have needed to explicitly declare eq_pa_r with an automatic lifetime—that is the implicit default. You need to create a standalone testcase, no one can run your code to try it out, plus it has many typos.

In reply to dave_59:

In reply to sriram.seshagiri:
You should not have needed to explicitly declare eq_pa_r with an automatic lifetime—that is the implicit default. You need to create a standalone testcase, no one can run your code to try it out, plus it has many typos.

Hi Dave,

Thanks for responding.

I have created a testable code anyone can run and, see. It is also in EDA playground: Automatic Variable example - EDA Playground

module top;
class dummy;

int curr_pid;
int eq_pa;

task body();

eq_pa = 'h100;
for(int i=0; i<10; i++) begin

fork
automatic int j = i;
automatic longint eq_pa_r = eq_pa;
//hiarb_hifis_req(eq_pa_r,j);
hiarb_hifis_req(eq_pa,i);

$display(“New EQ PAR : %0x\n”, eq_pa_r);
join_none;

eq_pa = eq_pa + 'h40;
$display(“New EQ PA : %0x\n”, eq_pa);
end
wait fork;
endtask

function void hiarb_hifis_req(int pa,int i);
  $display("Processing thread %0d with EQ PA: %0x",i,pa);

endfunction

endclass
initial begin
dummy dum = new();
dum.body();
end
endmodule

This is the Output I see without using Automatic variable here. Here my question is this should have worked right since like you said since it should have been by default Automatic within a Class entity in System Verilog.

Processing thread 10 with EQ PA: 380
New EQ PAR : 100

Processing thread 10 with EQ PA: 380
New EQ PAR : 140

Processing thread 10 with EQ PA: 380
New EQ PAR : 180

Processing thread 10 with EQ PA: 380
New EQ PAR : 1c0

Processing thread 10 with EQ PA: 380
New EQ PAR : 200

Processing thread 10 with EQ PA: 380
New EQ PAR : 240

Processing thread 10 with EQ PA: 380
New EQ PAR : 280

Processing thread 10 with EQ PA: 380
New EQ PAR : 2c0

Processing thread 10 with EQ PA: 380
New EQ PAR : 300

Processing thread 10 with EQ PA: 380
New EQ PAR : 340

But with using Automatic variable here I get the correct expected output:

Processing thread 0 with EQ PA: 100
New EQ PAR : 100

Processing thread 1 with EQ PA: 140
New EQ PAR : 140

Processing thread 2 with EQ PA: 180
New EQ PAR : 180

Processing thread 3 with EQ PA: 1c0
New EQ PAR : 1c0

Processing thread 4 with EQ PA: 200
New EQ PAR : 200

Processing thread 5 with EQ PA: 240
New EQ PAR : 240

Processing thread 6 with EQ PA: 280
New EQ PAR : 280

Processing thread 7 with EQ PA: 2c0
New EQ PAR : 2c0

Processing thread 8 with EQ PA: 300
New EQ PAR : 300

Processing thread 9 with EQ PA: 340
New EQ PAR : 340

In reply to sriram.seshagiri:

Perhaps you need to show the exact code that gives you unexpected results. My code on EDAPlayground shows the same results with and without the automatic keyword.

In reply to dave_59:

In reply to sriram.seshagiri:
Perhaps you need to show the exact code that gives you unexpected results. My code on EDAPlayground shows the same results with and without the automatic keyword.

Hi Dave,

fork
automatic int j = i;
automatic longint eq_pa_r = eq_pa;
//hiarb_hifis_req(eq_pa_r,j);
hiarb_hifis_req(eq_pa,i);

This is the code above which gives me unexpected results without using this Automatic switch. If you see I have commented the function call which I was calling by passing in these Automatic variables. And (highlighted in Bold) is the func call without using Automatic variables for which I am seeing this unexpected results.

Is this a simulator specifics (VCS only) limitation then ? Since you are saying you did not hit this issue even without using Automatic.

In reply to sriram.seshagiri:

It would have helped to show the exact code you are running in the first place. I finally understand your issue has nothing to do with default implicit lifetime—everything has automatic lifetime with the explicit keyword.

Your issue is simply which automatic variable you are using. There is only one instance of the
eq_pa
and
i
variables shared amongst all the threads. By the time any thread begins execution, those variables have reached their final values.

But when you declare automatic variables inside the for loop, each iteration creates a new instance of that variable using the current value of the initialization expression. So their are actually 10 different sets of
eq_pa_r
and
j
being passed to
hiarb_hifis_req()
each with a unique set of values.

In reply to dave_59:

Hi Dave,

Thanks. I think I kind of got your point. I kind of mixed two diff things and, got confused. Sorry for my confusion. My confusion was actually if the methods inside the class have an “Automatic lifetime”. Then for each of these threads the variables eq_pa and, i are also Automatic by default. But that does not make any sense since within a given instance there will be only 1 copy of these global variables which will get shared across all the threads.

So it is just that only the methods are Automatic by default in a class context like (For example this function: hiarb_hifis_req() in this case) and, we have multiple stack entries allocated for them for each call but, the variables (eq_pa and i) are shared by all threads and, we need to make it Automatic variable in this case to avoid this race condition. Also I believe the lifetime of this Automatic variable is very short and should disappear when leaving a scope. I think it may be active only till the procedural end of that scope (After that particular forked process thread ends).

Please kindly correct me if my understanding is incorrect here . Thanks Dave :)

In reply to sriram.seshagiri:

Class methods and loop variable declarations are the only places where variable lifetimes are implicitly automatic by default. The variable i is covered by both these rules. The lifetime of eq_pa is not automatic, it’s dynamically allocated when constructing the class object. But there is still only one instance of it shared among all threads.

The call stack from the function hiarb_hifis_req() has nothing to do with this issue. You would see the same problem changing the $display inside the fork/join_none to
$display(“New EQ PAR : %0x\n”, eq_pa);
.

There are no race conditions here—everything is deterministic. The key point is that variables with automatic lifetimes get created upon each entry into a scope and persist until all nested scopes exit. Those nested scopes created by the fork/join_none don’t even start until after the for-loop terminates.

In reply to dave_59:

In reply to sriram.seshagiri:
Class methods and loop variable declarations are the only places where variable lifetimes are implicitly automatic by default. The variable i is covered by both these rules. The lifetime of eq_pa is not automatic, it’s dynamically allocated when constructing the class object. But there is still only one instance of it shared among all threads.
The call stack from the function hiarb_hifis_req() has nothing to do with this issue. You would see the same problem changing the $display inside the fork/join_none to
$display(“New EQ PAR : %0x\n”, eq_pa);
.
There are no race conditions here—everything is deterministic. The key point is that variables with automatic lifetimes get created upon each entry into a scope and persist until all nested scopes exit. Those nested scopes created by the fork/join_none don’t even start until after the for-loop terminates.

Thanks Dave. Yea everything is deterministic here in the sense the for loop executes fast enough and, it has reached its final value before the threads even start executing. I was thinking earlier that since both execute at time 0 there might be a race condition between the for loop and, forked off threads. But simulation showed me that the for loop reaches it’s end value before even the threads start executing which means the threads start executing the last because of the fork … join_none() construct.

Also regarding your comment of “Class methods and loop variable declarations are the only places where variable lifetimes are implicitly automatic by default. The variable i is covered by both these rules”.

In this example I still see variable i not being implicitly treated as an automatic variable in the for loop. And I see the variable i getting shared by all the threads resulting in unexpected results.
That was the reason I previously declared an automatic variable j and assigned it to i so that it gets initialized on each iteration of the for loop.

module top;
class dummy;

  int curr_pid;
  int eq_pa;
 
  task body();
 
 eq_pa = 'h100;
    for(int i=1; i<10; i++) begin

fork
//automatic int j = i;
automatic longint eq_pa_r = eq_pa;
  hiarb_hifis_req(eq_pa_r,i)
  //hiarb_hifis_req(eq_pa,i);
  $display("New EQ PAR : %0x\n", eq_pa_r);
join_none;
 
eq_pa = eq_pa + 'h40;
  $display("New EQ PA : %0x\n", eq_pa);
  $display("value of i : %0d\n", i);
end
  wait fork;
endtask
  
    function void hiarb_hifis_req(int pa, int i);
      $display("Processing thread %0d with EQ PA: %0x",i,pa);
  endfunction
  
  endclass

Output
New EQ PA : 140

value of i : 1

New EQ PA : 180

value of i : 2

New EQ PA : 1c0

value of i : 3

New EQ PA : 200

value of i : 4

New EQ PA : 240

value of i : 5

New EQ PA : 280

value of i : 6

New EQ PA : 2c0

value of i : 7

New EQ PA : 300

value of i : 8

New EQ PA : 340

value of i : 9

Processing thread 10 with EQ PA: 100
New EQ PAR : 100

Processing thread 10 with EQ PA: 140
New EQ PAR : 140

Processing thread 10 with EQ PA: 180
New EQ PAR : 180

Processing thread 10 with EQ PA: 1c0
New EQ PAR : 1c0

Processing thread 10 with EQ PA: 200
New EQ PAR : 200

Processing thread 10 with EQ PA: 240
New EQ PAR : 240

Processing thread 10 with EQ PA: 280
New EQ PAR : 280

Processing thread 10 with EQ PA: 2c0
New EQ PAR : 2c0

Processing thread 10 with EQ PA: 300
New EQ PAR : 300

In reply to sriram.seshagiri:

Hi Dave,
Please kindly let me know regarding this. I am curious to know why it is not working for me here since, like you said i being a loop variable under a method it should default to an Automatic variable right.

Please let me know. Thanks.

In reply to sriram.seshagiri:

Like I said earlier, just declaring a variable automatic is not enough to fix the problem. You need to make sure there is a separate instance for each invocation of the thread. There is only one instance of the automatic variable i shared amongst all the threats. But there is a new instance of the variable j for every loop iteration/thread.

In reply to dave_59:

In reply to sriram.seshagiri:
Like I said earlier, just declaring a variable automatic is not enough to fix the problem. You need to make sure there is a separate instance for each invocation of the thread. There is only one instance of the automatic variable i shared amongst all the threats. But there is a new instance of the variable j for every loop iteration/thread.

Thanks Dave :) Makes sense to me. So to summarize basically loop variables and, even other variables declared within a method will default to Automatic. Say for example if eq_pa was declared inside the method; this will also default to a automatic variable and, this function will get cleared/killed from stack upon its execution.

But in this case I understood your point the Automatic variable i is declared only once and, it’s instance is shared amongst all the threads. But for Automatic variable j we create a new unique instance for every thread.

Thanks Dave. It is so nice to interact with you always. :)