1. First understand the concept of scope
Except for commonmodule
、interface
、class
、task
as well asfunction
Wait, and,begin-end blockandfork-join blockAlso onescope(Herefork-join blockincludefork-join
、fork-join_any
andfork-join_none
) As shown in the following example, a variable can also be declared in an anonymous block, which is only nested in the current anonymous block or inside it.scopeIt is visible in:
`timescale 1ns/1ns
module test;
int a = 1;
initial begin
int b;
b = 2;
$display("block scope b is %0d", b); // 2
end
// initial begin
// $display("try to access b : %0d", b); // Error
// end
initial begin
int a;
a = 3;
begin
$display("block scope a is %0d", a); // 3
end
end
initial fork
int a;
a = 4;
#0 $display("block scope a is %0d", a); // 4
join
initial begin
$display("module scope a is %0d", a); // 1
end
endmodule
2. Scope and lifetime
Reference IEEE Std 1800-20176.21Scope and lifetime, the original section is all the key points, please read it carefully.
- Variables declared inside a static task, function, or block are local in scope and default to a static lifetime.
- Specific variables within a static task, function, or block can be explicitly declared as automatic. Such variables have the lifetime of the call or block and are initialized on each entry to the call or block.
- Tasks and functions may be declared as automatic. 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. An automatic block is one in which declarations are automatic by default.
- Specific variables within an automatic task, function, or block can be explicitly declared as static. Such variables have a static lifetime.
- The lifetime of a fork-join block shall encompass the execution of all processes spawned by the block. The lifetime of a scope enclosing any fork-join block includes the lifetime of the fork-join block.
3. About the for-loop
- Reference : IEEE Std 1800-2017 12.7.1 The for-loop
- declared
for
loop variables are by defaultautomatic
- The variables used to control a for-loop can also be declared within the loop, as part of the for_initialization assignments. This creates an implicit begin-end block around the loop, containing declarations of the loop variables with automatic lifetime. This block creates a new hierarchical scope, making the variables local to the loop scope. The block is unnamed by default, but can be named by adding a statement label to the for-loop statement. Thus, other parallel loops cannot inadvertently affect the loop control variable.
module m;
initial begin
for (int i = 0; i <= 255; i++)
// something
end
initial begin
loop2: for (int i = 15; i >= 0; i--)
// something
end
endmodule
It is equivalent to the following code:
module m;
initial begin
begin
automatic int i;
for (i = 0; i <= 255; i++)
// something
end
end
initial begin
begin : loop2
automatic int i;
for (i = 15; i >= 0; i--)
// something
end
end
endmodule
4. Variable declarations in the static scope
Still refer to IEEE Std 1800-20176.21Scope and lifetime: Variables declared in a static task, function, or procedural block default to a static lifetime and a local scope. However, an explicit static keyword shall be required when an initialization value is specified as part of a static variable's declaration to indicate the user's intent of executing that initialization only once at the beginning of simulation. The static keyword shall be optional where it would not be legal to declare the variables as automatic. i.e.,For the defaultstatic
Statement: refers to when there is only variable declaration but not initialization.static
Keywords can be omitted; and when variable declarations contain initialized values, they should be specified explicitlystatic
orautomatic
Keywords (unlessautomatic
The statement is illegal, at this timestatic
Keywords can be omitted), otherwise a warning or an error will be reported. Here is the sample code:
`timescale 1ns/1ns
module test;
// A variable declaration that contains an initialization value
int svar0 = 1; // automatic is illegal, so static shall be optional
initial begin
// A variable declaration that contains an initialization value
int var1 = 1; // Warning: an explicit static/automatic needed
for (int i = 0; i < 3; i++) begin
// A variable declaration that contains an initialization value
int var2 = 1; // Error: an explicit static/automatic needed
end
end
endmodule
5. Analysis of a sample code snippet
If you understand the previous articlescope、lifetimeas well asfor-loop, can explain in IEEE Std6.21Examples of subsections:
`timescale 1ns/1ns
module test;
initial begin
for (int i=0; i<3; i++) begin
automatic int loop3 = 0;
for (int j=0; j<3; j++) begin
loop3++;
$display(loop3);
end
end // prints 1 2 3 1 2 3 1 2 3
// ------------------------------
for (int i=0; i<3; i++) begin
static int loop1 = 0;
for (int j=0; j<3; j++) begin
loop1++;
$display(loop1);
end
end // prints 1 2 3 4 5 6 7 8 9
end
endmodule : test
For descriptionforThe running process of a loop is roughly equivalent to the following code:
`timescale 1ns/1ns
module test;
initial begin
begin
automatic int i;
i = 0;
begin
automatic int loop3 = 0;
for (int j=0; j<3; j++) begin
loop3++;
$display(loop3);
end
end
i = 1;
begin
automatic int loop3 = 0;
for (int j=0; j<3; j++) begin
loop3++;
$display(loop3);
end
end
i = 2;
begin
automatic int loop3 = 0;
for (int j=0; j<3; j++) begin
loop3++;
$display(loop3);
end
end
i = 3;
end
// ---------------------------------
begin
automatic int i;
static int loop1 = 0; // static lifetime
// loop1 only be initialized once on first entry to the call or block
i = 0;
begin
for (int j=0; j<3; j++) begin
loop1++;
$display(loop1);
end
end
i = 1;
begin
for (int j=0; j<3; j++) begin
loop1++;
$display(loop1);
end
end
i = 2;
begin
for (int j=0; j<3; j++) begin
loop1++;
$display(loop1);
end
end
i = 3;
end
end
endmodule : test
If you only look at the second half of the code, change it slightly as follows:
`timescale 1ns/1ns
module test;
initial begin
for (int i=0; i<3; i++) begin
static int local_i = i; // Error
for (int j=0; j<3; j++) begin
local_i++;
$display(local_i);
end
end
end
endmodule : test
An error will be reported here: A static declaration may not use any non-static references in its initial expression, that is, thestatic
In variable declaration, the initialization expression cannot be referenced.automatic
variable. We delete the initialization expression when declaring the variable, modify it as follows, let’s analyze the output result:
`timescale 1ns/1ns
module test;
initial begin
for (int i=0; i<3; i++) begin
static int local_i; // no explicit initialization
local_i = i;
for (int j=0; j<3; j++) begin
local_i++;
$display(local_i);
end
end
end
endmodule : test
At this time, its output is1 2 3 2 3 4 3 4 5
, Similarly, expand the loop, which is roughly equivalent to the following code:
`timescale 1ns/1ns
module test;
initial begin
begin
automatic int i;
static int local_i;
i = 0;
begin
local_i = i;
for (int j=0; j<3; j++) begin
local_i++;
$display(local_i);
end
end
i = 1;
begin
local_i = i;
for (int j=0; j<3; j++) begin
local_i++;
$display(local_i);
end
end
i = 2;
begin
local_i = i;
for (int j=0; j<3; j++) begin
local_i++;
$display(local_i);
end
end
i = 3;
end
end
endmodule : test
byC
The language comparison is likeC
The static local variable declared by the language in the function is the same. This variable allocates memory in the global data area (static area). It always resides in the global data area, and its life cycle is until the end of the program run (have a static lifetime instead of the lifetime of the call or block), but pay attention to its scope (scope) is still in the local scope; the static local variable is initialized for the first time when the program executes to the declaration of the variable, and subsequent function calls are no longer initialized (only be initialized once on first entry to the call or block), we usually initialize it when declaring (such asstatic int local_i = 0;
), if not explicitly initialized, it will be automatically initialized to its default value by the program (IEEE Std 1800-2017Table 6-7Default variable initial values). In addition, you need to be careful not to confuse initialization and assignment. Initialization is different from variable assignment. Even static variables can be assigned multiple times, but static variables will only be initialized once.
6. loop with fork-join block
If the loop containsfork-join block, the results may sometimes be different from expectations. Similarly, we can expand the loop to see the running process of the program.fork-join blockIncludefork-join
、fork-join_any
andfork-join_none
, let's look at it separately.
6.1 loop with fork-join
block
`timescale 1ns/1ns
module test;
initial begin
// something
for (int i = 0; i < 3; i++)
fork
#1 $display("@%0t : %0d", $time(), i);
#2 $display("@%0t : %0d", $time(), i);
#3 $display("@%0t : %0d", $time(), i);
join
// something
end
endmodule : test
forfork-join
Block, the output result is determined at this time, as follows:
# @1 : 0
# @2 : 0
# @3 : 0
# @4 : 1
# @5 : 1
# @6 : 1
# @7 : 2
# @8 : 2
# @9 : 2
This is becausefork-join
The block will wait for all the generated child processes to be completed before the control will be returned to the parent process, so it can be understood as expanding with the previous loop.begin-end blockSame because in loop control variables (as in the examplei
) will remain unchanged and wait for the current one until update to the next valuebegin-end blockorfork-join
All blocks are executed, the difference isbegin-end blockThe subprocesses within are executed sequentially, andfork-join
The child processes within the block are executed in parallel. The above code is roughly equivalent to the following code:
`timescale 1ns/1ns
module test;
initial begin
// something
begin
automatic int i;
i = 0;
fork
#1 $display("@%0t : %0d", $time(), i);
#2 $display("@%0t : %0d", $time(), i);
#3 $display("@%0t : %0d", $time(), i);
join
i = 1;
fork
#1 $display("@%0t : %0d", $time(), i);
#2 $display("@%0t : %0d", $time(), i);
#3 $display("@%0t : %0d", $time(), i);
join
i = 2;
fork
#1 $display("@%0t : %0d", $time(), i);
#2 $display("@%0t : %0d", $time(), i);
#3 $display("@%0t : %0d", $time(), i);
join
i = 3;
end
// something
end
endmodule : test
6.2 loop with fork-join_any
block
And forfork-join_any
andfork-join_none
, there is a little difference, let's take a look firstfork-join_any
block, which is described asThe parent process blocks until any one of the processes spawned by this fork completes, iffork-join_any
There is only one child process in the block, and its behavior isfork-join
The blocks are exactly the same, as shown in the following example:
`timescale 1ns/1ns
module test;
initial begin
for (int i = 0; i < 3; i++)
fork
#1 $display("@%0t : %0d", $time(), i);
join_any
end
endmodule : test
The output looks like this:
# @1 : 0
# @2 : 1
# @3 : 2
Similarly, just expand the loop, which is roughly equivalent to the following code:
`timescale 1ns/1ns
module test;
initial begin
begin
automatic int i;
i = 0;
fork
#1 $display("@%0t : %0d", $time(), i);
join_any
i = 1;
fork
#1 $display("@%0t : %0d", $time(), i);
join_any
i = 2;
fork
#1 $display("@%0t : %0d", $time(), i);
join_any
i = 3;
end
end
endmodule : test
For the previous articlefork-join
The sample code in the block, we will directlyfork-join
Change tofork-join_any
, at this timefork-join_any
The block contains more than one child process, as shown below:
`timescale 1ns/1ns
module test;
initial begin
// something
for (int i = 0; i < 3; i++)
fork
#1 $display("@%0t : %0d", $time(), i);
#2 $display("@%0t : %0d", $time(), i);
#3 $display("@%0t : %0d", $time(), i);
join_any
// something
end
endmodule : test
The output at this time seems a bit confusing:
# @1 : 0
# @2 : 1
# @2 : 1
# @3 : 2
# @3 : 2
# @3 : 2
# @4 : 3
# @4 : 3
# @5 : 3
In fact, as mentioned abovefork-join
The equivalent code of the block sample code, forfork-join_any
, we can still directly expand the loop and get the following code (the tags marked with comments are convenient for us to explain the execution order of the code):
`timescale 1ns/1ns
module test;
initial begin
// something
begin
automatic int i;
i = 0;
fork // fork_A
#1 $display("@%0t : %0d", $time(), i); // A_1
#2 $display("@%0t : %0d", $time(), i); // A_2
#3 $display("@%0t : %0d", $time(), i); // A_3
join_any
i = 1;
fork // fork_B
#1 $display("@%0t : %0d", $time(), i); // B_1
#2 $display("@%0t : %0d", $time(), i); // B_2
#3 $display("@%0t : %0d", $time(), i); // B_3
join_any
i = 2;
fork // fork_C
#1 $display("@%0t : %0d", $time(), i); // C_1
#2 $display("@%0t : %0d", $time(), i); // C_2
#3 $display("@%0t : %0d", $time(), i); // C_3
join_any
i = 3;
end
// something
end
endmodule : test
- First execute to
fork_A
and block the current variablei
for0
, wait for any child process to complete,1ns,A_1
Complete first, print@1 : 0
And return to the parent process, there are two processes in the backgroundA_2
andA_3
Waiting for dispatch - The parent process executes
fork_B
and block, at this time the variablei
Has become1
, and three child processes were generated at the same timeB_1
、B_2
andB_3
, and pass again1ns,A_2
andB_1
Scheduled, so print two@2 : 1
, there are three processes in the background at this timeA_3
、B_2
andB_3
Waiting for dispatch - because
B_1
Finish,fork_B
Go back to the parent process and executefork_C
, three more child processes were generatedC_1
、C_2
andC_3
, at this time the variablei
Has become2
, and pass again1ns,at this timeA_3
、B_2
andC_1
Scheduled, so three are printed out@3 : 2
, there are three processes in the background at this timeB_3
、C_2
andC_3
Waiting for dispatch - because
C_1
Finish,fork_C
Go back to the parent process and loop over to control the variablesi
Become3
, no longer meets the loop condition, so exitfor
Loop, and there are three processes in the background at this timeB_3
、C_2
andC_3
Waiting for dispatch, passing1ns,B_3
andC_2
It is scheduled to execute, so two are printed out@4 : 3
, pass again1ns,C_3
was scheduled to execute, so finally printed out@5 : 3
, the simulation ends
So how to make the abovefork-join_any
Block sample code output 3 pieces0
, 31
And 32
? Refer to the previous articlebegin-end blockThe routine, similar, we can stillfork-join blockUsed inautomatic
Variables to loop variables within blocksi
Make a local copy, as shown in the following example:
`timescale 1ns/1ns
module test;
initial begin
// something
for (int i = 0; i < 3; i++)
fork
// local copy, local_i, for each value of i
automatic int local_i = i;
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_any
// something
end
endmodule : test
The output result at this time is as follows:
# @1 : 0
# @2 : 0
# @2 : 1
# @3 : 0
# @3 : 1
# @3 : 2
# @4 : 1
# @4 : 2
# @5 : 2
According to our expectations, 3 outputs0
, 31
And 32
, and the timing is as followsfork-join_any
The block example is exactly the same, and will not be analyzed again here. The execution process is roughly equivalent to the following code:
`timescale 1ns/1ns
module test;
initial begin
// something
begin
automatic int i;
i = 0;
fork
// local copy, local_i, current value of i is 0
automatic int local_i = 0; // local local_i is 0
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_any
i = 1;
fork
// local copy, local_i, current value of i is 1
automatic int local_i = 1; // local local_i is 1
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_any
i = 2;
fork
// local copy, local_i, current value of i is 2
automatic int local_i = 2; // local local_i is 2
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_any
i = 3;
end
// something
end
endmodule : test
Here, the reason for controlling variables to loopsi
The local copy of the program is valid because when the program enters a block or scope, the variable declaration in the scope will be processed first. If the variable declaration contains explicit initialization, it will be initialized accordingly, otherwise it will be initialized to the default. value. It can be understood that unlike procedural code, declarative code is processed in parallel when entering the current scope, and this process has no overhead (such as not being able to break points, and no child processes will be generated).
From this, it is derived from the previousbegin-end blockDifferent, here are two points worth noting:
- NoticeInitialize explicitly when variable declarationas well asDeclare the variable first and then assign it to the valueThe difference between
- Aboutfork-join blockProblem of using static variables within (note the life cycle of static variables)
In the previous articlebegin-end blockThere is no problem in the example, butfork-join blockThere may be unexpected results in this case. The following will illustrate these two points separately.
Regarding point 1, if it is not explicitly initialized when the variable is declared, it is declared first and then assigned as shown in the following example:
`timescale 1ns/1ns
module test;
initial begin
// something
for (int i = 0; i < 3; i++)
fork
// local copy, local_i, for each value of i
automatic int local_i; // default initialization
local_i = i; // assignment statement
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_any
// something
end
endmodule : test
The output of this program is as follows:
# @1 : 2
# @1 : 1
# @1 : 0
# @2 : 2
# @2 : 1
# @2 : 0
# @3 : 2
# @3 : 1
# @3 : 0
Actually, what I want to explain here is thatNotice:fork-join blockWill alsoAn assignment statement creates a child process,butVariable declarations do not produce processes(even variable declarations with initialization expressions). In fact, you will find thatC
The language is the same. For example, we can also execute it step by step during simulation. You will see that the program pointer will always skip the variable declaration directly. Or write out the roughly equivalent code of the program to explain, as follows:
`timescale 1ns/1ns
module test;
initial begin
// something
begin
automatic int i;
i = 0;
fork // <--------------------------------+
automatic int local_i; // |
local_i = i; // <--------------------------------+ A0
#1 $display("@%0t : %0d", $time(), local_i); // | A1
#2 $display("@%0t : %0d", $time(), local_i); // | A2
#3 $display("@%0t : %0d", $time(), local_i); // v A3
join_any // <--------------------------------+
i = 1; // |
fork // <--------------------------------+
automatic int local_i; // |
local_i = i; // <--------------------------------+ B0
#1 $display("@%0t : %0d", $time(), local_i); // | B1
#2 $display("@%0t : %0d", $time(), local_i); // | B2
#3 $display("@%0t : %0d", $time(), local_i); // v B3
join_any // <--------------------------------+
i = 2; // |
fork // <--------------------------------+
automatic int local_i; // |
local_i = i; // <--------------------------------+ C0
#1 $display("@%0t : %0d", $time(), local_i); // | C1
#2 $display("@%0t : %0d", $time(), local_i); // | C2
#3 $display("@%0t : %0d", $time(), local_i); // v C3
join_any // <--------------------------------+
i = 3;
end
// something
end
endmodule : test
Previous examples, eachfork-join_any
The block generates 3 subprocesses (i.e., display statements with 3 striped delay control), and now eachfork-join_any
The block contains 4 subprocesses (we have an additional assignment statement without adding delay), and the execution process is:
- Enter the first one
fork-join_any
Block, assignment statementlocal_i = i;
Immediately scheduled to execute due to the process (A0
) is not time-consuming, so the execution is completed and returned to the parent process. At this time, there are already three child processes in the background (A1
、A2
andA3
) Waiting for execution to be scheduled - Enter the second
fork-join_any
block, then the same as above, the same as, all the way to the loop variablei
To be 3, exit the loop, at this time, there are 9 subprocesses in the backgroundA1
、A2
、A3
、B1
、B2
、B3
、C1
、C2
andC3
Waiting for dispatched execution - go through1ns,
A1
、B1
andC1
The order between them is scheduled to be executed at the same time, and the order between them is uncertain (Nondeterminism): In my Questa Sim simulation test, its execution order isC1
-B1
-A1
, so the print result is@1 : 2
-@1 : 1
-@1 : 0
; and in VCS simulation test, its execution order isA1
-B1
-C1
, so the print result is@1 : 0
-@1 : 1
-@1 : 2
- Passed again1ns,
A2
、B2
andC2
Simultaneously scheduled execution; similarly,1ns,A3
、B3
andC3
It is scheduled to execute at the same time, and the simulation ends
Actually, here we are usingfork-join_any
The original intention is to wait for any child process of the display statement with 3 striped delay control to return to control and enter the next round of loop. However, due to the existence of the assignment statement, its final behavior is the same asfork-join_none
Same, because the assignment statement will also produce a child process, and no delay or other blocking statements are added here. So, if you just want tofork-join blockMake local copies of external variables, please explicitly initialize them when declaring the variable.
Finally, again, infork-join blockWe must pay attentionDeclare variables and initialize themandAssignment statementThe difference between. Depend onfork
The generated child processes are executed in parallel, and an assignment statement is also an ordinary child process, waiting for dispatch and execution like other child processes; but for variable declarations, no process is generated, as long as it is executedfork
,currentfork-join blockAll variable declarations in the following will be processed immediately, and if the variable declaration is accompanied by an explicit initialization value expression, the variable will be properly initialized, otherwise it will be initialized to its default value (IEEE Std 1800-2017Table 6-7Default variable initial values). In fact, includingbegin-end blockandfork-join block, even anyscope, when the program execution enters thescopeWhen inside, the first thing to do isscopeAll variable declarations within are initialized, but their internal nestedscopeThe variable declaration inside is not specified unless the program execution enters the internal nestedscopeThis will only be considered withinscopeThe variable declarations within are initialized.
Regarding point 2, let’s explain it nowfork-join blockThe problem of using static variables is still tofork-join_any
Block sample code output 3 pieces0
, 31
And 32
, Can static variables be used to make local copies? The following code looks like:
`timescale 1ns/1ns
module test;
initial begin
// something
for (int i = 0; i < 3; i++)
fork
static int local_i;
local_i = i;
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_any
// something
end
endmodule : test
The output result at this time is as follows:
# @1 : 2
# @1 : 2
# @1 : 2
# @2 : 2
# @2 : 2
# @2 : 2
# @3 : 2
# @3 : 2
# @3 : 2
First of all, we have already introduced it in the previous article.staticThe initialization expression of a variable cannot be referencedautomaticVariables, so when using static variables here, you can only declare them first, and then make local copying through assignment statements. This is also back to the previous discussion issue. Please read the specific explanation in the previous example; secondly, why at the endlocal_i
All are 2, in factstaticVariables andautomaticThe difference between variables, namely the life cycle and scope issues, has also been introduced and explained in the previous article. I will not repeat it here. Let's directly expand its specific execution process to explain it, which is roughly as follows. , just read the code to understand its output:
`timescale 1ns/1ns
module test;
initial begin
// something
begin
automatic int i;
static int local_i;
i = 0;
fork
local_i = i;
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_any
i = 1;
fork
local_i = i;
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_any
i = 2;
fork
local_i = i;
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_any
i = 3;
end
// something
end
endmodule : test
You may find that all of the above includestaticIn the example of variable declaration, when we analyze the general execution process, we willstaticThe declaration of variables is placed with multipleblockSide byscopeThis is actually for simulationstaticVariablesscopeandlifetime,andstaticThe variable will only be initialized once. existscope and lifetimeIn the subsection, it has been used several timesentry of the call or blockIn fact, this can be roughly the same asC
The function call is used as an analogy, but inC
There are only functions in it, and we havecall : { function & task} as well asblock : { begin-end block & fork-join block}, so besidescallIn addition, we enter one at a timeblock, it can also be roughly understood as performing oneC
function calls, so besidesfunctionandtask, we areblockIn-housestaticVariables can also be roughly equivalent toC
Static local variables within a function. With these analogies, if you are familiar with themC
, then the understanding of all the above examples will be clearer. For example, infork-join blockstatic variables are used to make thisblockNot reentered【Please refer toReentrable function(reentrant function) concept], especially forfork-join_any
andfork-join_none
, defined in a loopfork-join_any
orfork-join_none
When blocking, we usually want toblockis reentrant, so don't use static variables inside it, in other words, in concurrent threads (fork-join block) When declaring variables, be sure to useautomaticVariables to save numeric values.
6.3 loop with fork-join_none
block
Finally, let's take a lookfork-join_none
block, which is described asThe parent process continues to execute concurrently with all the processes spawned by the fork. The spawned processes do not start executing until the parent thread executes a blocking statement or terminates. Pay attention to the second sentence, which is different fromfork-join
andfork-join_any
, they metjoin
orjoin_any
When keywords arefork
All the generated child processes are scheduled to be executed; and forfork-join_none
, when encounteringjoin_none
When keywords are used, control is immediately returned to the parent process, but at this timefork
The generated child processes do not start to be scheduled execution immediately, but will not start to be scheduled execution until the parent process continues to execute until a blocking statement or the parent process terminates. Let's give a simple example, as follows:
`timescale 1ns/1ns
module test;
int m;
initial begin
fork
m = 0;
#0 $display("@%0t : %0d", $time(), m);
join_none
m = 1;
$display("@%0t : %0d", $time(), m);
end
endmodule : test
The program will always output1 0
, because enteringfork
After that, two child processes are created and then encounteredjoin_none
Keyword, return to the parent process to continue execution, but at this timefork
The two child processes created did not start executing immediately, and the parent process continued to execute and successivelym = 1;
anddisplay
The statement was printed out1
Since neither of these two statements will cause blockage, thefork
The two child processes created have not started executing untilbegin-end
The block ends and the parent process terminates, and the two child processes start to execute in parallel, and because#0
The existence of the two parallel child processes,display
The process is always therem = 0;
The process is scheduled to execute afterwards, so it is printed again0
。
After understanding the examples in the previous text, the loop includesfork-join_none
The execution process should be very clear, so let’s take a look at an example, the code is as follows:
`timescale 1ns/1ns
module test;
initial begin
for (int i = 0; i < 3; i++)
fork
// local copy, local_i, for each value of i
automatic int local_i = i;
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_none
end
endmodule : test
In Questa Sim simulation, the output is as follows:
# @1 : 2
# @1 : 1
# @1 : 0
# @2 : 2
# @2 : 1
# @2 : 0
# @3 : 2
# @3 : 1
# @3 : 0
The execution process is roughly consistent with the following code:
`timescale 1ns/1ns
module test;
initial begin
begin
automatic int i;
i = 0;
fork
automatic int local_i = 0; // initialization
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_none
i = 1;
fork
automatic int local_i = 1; // initialization
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_none
i = 2;
fork
automatic int local_i = 2; // initialization
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_none
i = 3;
end
end
endmodule : test
Every time you enterfork
Block, first initialize the variables declared in the block, and then create a child process until it encountersjoin_none
Keywords, return directly to the parent process and continue execution, and finally start executing the child process:1nsWhen three subprocesses are scheduled to execute at the same time, the order of the sequence is uncertain (in the simulation scheduling semantics of SV), different emulators may have different output results, such as output in Questa Sim simulation test@1 : 2 1 0
, while output in VCS simulation test@1 : 0 1 2
;2nsand3nsAt the same time, the same applies.
6.4 Comparative summary of loop with block
If the comparison is summarizedbegin-end blockandfork-join block, in fact, you canblockAnalogyC
to understand the functions in. Every time you enterblock, first of allblockThe variable declared within is initialized (note: forstaticVariable declaration, only for the first timeblockThe variable is initialized only when it is initialized), you need to pay attention to its life cycle, and then:
- in the case ofbegin-end block, then the statements in the block are executed in sequence directly (statement)
- in the case offork-join block, only subprocesses are created for statements in the block (statement) and not executed immediately
- for
fork-join
block until encounterjoin
Keywords: At this time, all child processes in the block have been created, and these child processes start to be executed in parallel, and the parent process is blocked here. You have to wait for these child processes to complete before continuing to execute. - for
fork-join_any
block until encounterjoin_any
Keyword, at this time, all child processes in the block have been created, and these child processes start to be executed in parallel, and the parent process is blocked here. You have to wait for any child processes of these child processes to complete before continuing to execute. The remaining child processes will continue to be executed in parallel with the parent process in the background. - for
fork-join_none
block until encounterjoin_none
Keyword, at this time all child processes in the block have been created, but these child processes do not start parallel execution immediately, and the parent processNoIt is blocked here, but continues to execute directly. It does not start executing these child processes in parallel until it encounters a blocking statement or terminates. At this time, all child processes and the parent processes are executed in parallel.
- for
7. Supplement
In IEEE Std9.3.2Parallel blocks are explained:Variables declared in the block_item_declaration of a fork-join block shall be initialized to their initialization value expression whenever execution enters their scope and before any processes are spawned.
We have the sample code as follows:
`timescale 1ns/1ns
module test;
initial begin
for (int i = 0; i < 3; i++)
fork
// something
begin
automatic int local_i = i;
#local_i $display("@%0t : %0d", $time(), local_i);
end
// something
join_none
end
endmodule : test
So, herelocal_i
The value of this is uncertain, or herelocal_i
For variablesi
The local copy of the variable is not as expected and there is no correct save to the variablei
The output result of the program is as follows:
# @3 : 3
# @3 : 3
# @3 : 3
Here, we just add an example. In fact, we have already explained everything in the previous article:
- Only when the program execution entersscopeOnly when inside, will it be correctscopeAll variables declared within are initialized
-
fork
createfork-join blockThe child processes are not executed at the same time.
So in this examplefork-join_none
Only one child process is created in the block, but the creation of the child process does not schedule the execution of the child process, so in fact the program has not yet been executed and entered into thebegin-end
In the block, it will not be correctbegin-end
The variable declaration in the block is initialized. The program will only enter when the created child process starts executing.begin-end
In the block and initialize all variables declared in the block, and at this time the variablesi
The value of , relative to the moment the child process was created, has changed.
We just need tofork-join blockJust make a local copy as shown below:
`timescale 1ns/1ns
module test;
initial begin
for (int i = 0; i < 3; i++)
fork
automatic int local_i = i;
// something
begin
#local_i $display("@%0t : %0d", $time(), local_i);
end
// something
join_none
end
endmodule : test
The output result of the program at this time is as follows:
# @0 : 0
# @1 : 1
# @2 : 2
In addition, we can also use thelocal_i
Then make a local copy to isolate each child process pairlocal_i
The example of changes is as follows:
`timescale 1ns/1ns
module test;
initial begin
for (int i = 0; i < 3; i++)
fork
automatic int local_i = i;
// something ...
begin
// local copy, process_i, for value of local_i
// do not copy from i, otherwise, the value of process_i is undetermined
automatic int process_i = local_i;
#process_i $display("@%0t : %0d", $time(), process_i);
// we can access or modify variable process_i here
// ... ... use process_i here
end
begin
automatic int process_i = local_i; // this is ok
// ... ... use process_i here
end
// something ...
join_none
end
endmodule : test
Except directly infork-join blockWe often see the following writing method when making local copies:
`timescale 1ns/1ns
module test;
initial begin
for (int i = 0; i < 3; i++) begin
automatic int local_i = i;
fork
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_none
end
end
// Previous Example :
// --------------------------------------------------
// initial begin
// for (int i = 0; i < 3; i++)
// fork
// automatic int local_i = i;
// #1 $display("@%0t : %0d", $time(), local_i);
// #2 $display("@%0t : %0d", $time(), local_i);
// #3 $display("@%0t : %0d", $time(), local_i);
// join_none
// end
// --------------------------------------------------
endmodule : test
Please compare and understand these two writing methods and give a brief explanation. Here is an extra layerscope,existfork-join_none
Another outsidebegin-end
,at this time,automatic int local_i = i;
Will be able tofork-join_none
Take it out of the block, then the variablei
Will be on the previous levelscopeis saved in, compared to the previous example, the program will now be executed in sequence each time the loop isbegin-end
In the blockfork
The advantage of all previous statements is that it is not limited to the declaration and initialization statement of local variables. Before creating parallel child processes, we can also execute some other programs, such as configuring the environment, etc., as follows:
`timescale 1ns/1ns
module test;
initial begin
for (int i = 0; i < 3; i++) begin
automatic int local_i = i;
$display("@%0t : before creating the child process.", $time());
// Any statement, to write something as an example
local_i++;
if (local_i > 2)
local_i = 0;
// You can also do some other things here
// ... ... ...
// Statements shall be executed in sequence
// ... ... ...
$display("@%0t : start creating the child process.", $time());
fork
#1 $display("@%0t : %0d", $time(), local_i);
#2 $display("@%0t : %0d", $time(), local_i);
#3 $display("@%0t : %0d", $time(), local_i);
join_none
end
end
endmodule : test