Wait for only some threads to complete after fork join_none in SystemVerilog - system-verilog

In SystemVerilog I need to wait for some threads that have been executed inside a fork join_none structure to finish. But there is another process inside another fork join_none structure that will never end.
My code looks like this:
fork
process_that_will_never_end();
join_none
fork
for(int i = 0; i < 40; i++) begin
fork
process_that_must_end(i);
join_none
end
join
The fork join containing the for loop has no effect (which is what I expected). I thought about using a "wait fork" at the end but this will also wait for process_that_will_never_end(), so it won't work.
Is there any way to wait only for all the process_that_must_end() threads to finish?

You almost had it. You just need to move the scope of the begin/end to outside the for loop. Then the wait fork only applies to the children of the second fork.
fork : first_fork
process_that_will_never_end();
join_none
fork begin : second_fork
for(int i = 0; i < 40; i++)
fork : third_forks
automatic int k = i;
process_that_must_end(k);
join_none
wait fork;
end join

Related

Will infinite while loop inside fork-join_any end after exiting?

Will the first while loop continue after I have exited fork/join_any? I see the message "iam here" after the message "after join any".
I expect that the first while loop will stop when I exit fork/join_any.
Is that not the case?
fork
begin
while(1)
begin
#(posedge trigger_clock);
do something
$info("i am here");
end
end
begin
while(1)
begin
#(posedge trigger_clock);
if(condition is true)
begin
break;
end
end
end
join_any
$info("after join any");
As you have observed, the top while loop continues to execute even when you exit the fork when the bottom while loop completes. This is expected.
If you want the top while loop to stop executing when you exit the fork, you can use the disable fork statement:
fork
// ...
join_any
disable fork;
$info("after join any");
Refer to IEEE Std 1800-2017, section 9.6 Process control.

fork join within for loop in system verilog

Problem: What will be the output of this code? and why?
module tb;
int i;
initial begin
for(i=0; i<10; i++)
begin
fork
#1 $display("Value = %d", i);
join_none
end
end
endmodule
To spawn the fork threads with all the possible index values, you can use automatic variable inside for loop,
module tb;
int i;
initial begin
for(i=0; i<10; i++)
begin
automatic int j = i;
fork
#1 $display("Value = %d", **j**);
join_none
end
end
endmodule
Output:
Value = 0
Value = 1
Value = 2
Value = 3
Value = 4
Value = 5
Value = 6
Value = 7
Value = 8
Value = 9
EDA Playground link
To understand the automatic variable lifetime concept including 'fork inside for', please refer to SV LRM "6.21 Scope and lifetime" or you can find many threads on this topic such as: fork join_none inside for loop
The example starts 10 parallel threads of $display and schedules each #1 in the future. They print the value 10 because there is no time to block (join_none does not block at all) the loop so the loop executes all its iterations at t=0.
Added some printing of the execution time so that its easier to see what is happening. The thread of execution does not wait for any of them to finish (join_none) so the final print executes at t=0;
The simulation output is here
If you need further research see IEEE 1800-2017 section 9.3.2 parallel blocks.
https://standards.ieee.org/standard/1800-2017.html
If you want to run it go to www.edaplayground.com,and you can run it with the industry standard tools almost free (need to register with an email).

UVM error when using multiple sequencers using for loop construct

I have a sort of following code in the body of my UVM virtual seq:
begin:
for(int x=0; x<8; x++) begin
fork begin
automatic int x_idx = x;
for(int i=0; i<100; i++) begin
if(!my_sequence[x_idx].randomize() with {...}
my_sequence[x_idx].start(p_sequencer.my_sequencer[x_idx];
end
end join_none
end
end
Code information:
I have 8 sequencers each receiving its own sequence. Hence the first "for loop" with 8 iterations.
After going through some tutorials I understood that using fork-join I can run the 8 sequences to the 8 sequencers in parallel.
But when I run the code I get error on the "if(!my_sequence..." line of code. Error being:
The object is being used before it was constructed/allocated.
I also tested the code by getting rid of the outer for loop and instead using index numbers as follows:
fork begin
automatic int x_idx = x;
for(int i=0; i<100; i++) begin
if(!my_sequence[0].randomize() with {...}
my_sequence[0].start(p_sequencer.my_sequencer[0];
end
end join_none
fork begin
automatic int x_idx = x;
for(int i=0; i<100; i++) begin
if(!my_sequence[0].randomize() with {...}
my_sequence[1].start(p_sequencer.my_sequencer[1];
end
end join_none
...
This seems to work. But I want to write better code with for loop.
Another info: While debugging using DVE I saw the x_idx value to be 8. Not sure why it is showing as 8 and also not sure if that's the reason for the bug.
Your problem is you placed the x_idx declaration inside the begin/end block that is inside the fork/join_none. You don't want the initialization to happen when the parallel threads start executing; you want it to happen as each iteration of the for loop executes. You should write it as
begin
for(int x=0; x<8; x++)
fork
int x_idx = x;
for(int i=0; i<100; i++) begin
if(!my_sequence[x_idx].randomize() with {...}
my_sequence[x_idx].start(p_sequencer.my_sequencer[x_idx];
end
join_none
end
Also, there's no need to use the automatic keyword inside a class—all variables declared inside a class method have an implicit automatic lifetime.
Note that declarations inside fork/join's initialize upon hitting the fork block, not when the statements inside the fork begin their threads. The declarations are not separate threads. You could also write as:
begin
for(int x=0; x<8; x++) begin
int x_idx = x;
fork
for(int i=0; i<100; i++) begin
if(!my_sequence[x_idx].randomize() with {...}
my_sequence[x_idx].start(p_sequencer.my_sequencer[x_idx];
end
join_none
end
end
In both cases, there is a new instance of x_idx for every loop iteration, but the statements inside the fork/join_none don't begin executing until after all iterations complete.

How to prevent new threads of SVA

Lets assume, I have a button in my design. I want to increment counter between next two clock when button has been pressed three times and I want to check this behaviour with SVA.
I have wrote this one:
`timescale 1ns / 1ps
module tb();
parameter NUMBER_OF_PRESSES = 10;
parameter CLK_SEMI_PERIOD = 5;
bit clk;
always #CLK_SEMI_PERIOD clk = ~clk;
bit button_n;
bit reset_n;
logic [7:0] counter;
property p;
logic[7:0] val;
disable iff(!reset_n) #(posedge clk) (($fell(button_n)[=3]),val=counter) |=> ##[0:2] (counter== val+1);
endproperty
assert property(p);
initial begin
automatic bit key_d;
automatic byte key_lat;
automatic byte key_press_count;
reset_n = 1;
button_n = 1;
counter = 0;
fork
begin
repeat(NUMBER_OF_PRESSES) begin
repeat(5)begin
#(negedge clk);
end
button_n = 0;
key_lat = $urandom_range(1,4);
repeat(key_lat) begin
#(negedge clk);
end
button_n = 1;
end
end
begin
forever begin
#(posedge clk);
if(!button_n && key_d) begin
key_press_count++;
end
if(key_press_count == 3) begin
counter++;
key_press_count = 0;
end
key_d = button_n;
end
end
join_any
end
endmodule
This works good at first three press, but then it will always throw assertion error, because it has been started new thread of assertion at each button press. So, I need to prevent testbench from doing this. When repetitition has been started I don't need to start new threads.
How can I do this?
I am not confident I fully understand your question. Let me first state my understanding and where I think your problem is. Apologise if I am mistaken.
You intend to detect negedges on button_n ("presses"), and on the third one, you increment "counter".
The problem here is that your stated objective (which actually matches the SVA) and your design do different things.
Your SVA will check that the counter has the expected value 1-3 cycles after every third negedge. This holds for press 0, 1 and 2. But it must also hold for press 1, 2 and 3. And press 2, 3 and 4 etc. I suspect the assertion passes on press 2 and then fails on press 3. I.e. you check that you increment your counter on every press after the third.
Your design, on the other hand does something different. It counts 3 negedges, increments counter, and it then starts counting from scratch.
I would advise against the use of local variables in assertions unless you are certain that it is what you need - I don't think this is the case here. You can have your SVA trigger on key_press_count == 3 (assuming you ofc define key_press_count appropriately and not as an automatic var).
If you insist on using your local SVA variable you can slightly modify your trigger condition to include counter. For example something along the lines of (though may be slightly wrong, have not tested):
(counter == 0 || $changed(counter)) ##1 ($fell(button_n)[=3], val = counter)
IMO that's a bad idea and having supporting RTL is the better way to go here to document your intention as well as check exactly the behaviour you are after.
Hope this helps

How to access generate block elements hierarchically

Consider code in SV interface:
genvar i;
generate
for (i = 0; i < 2; i++) begin : g1
task ab ();
///< something here
endtask
end
endgenerate
According to LRM 1800-2012 section 27.6 I should technically be able to access this task by (consider in monitor vif is a virtual instance of interface):
vif.g1[i].ab();
Is this correct? If yes then simulators have issue supporting it since it reports vif.g1 can't be found (elaboration time)
I've tried it without named block as well it just suppose to be:
vif.genblk1[i].ab();
but no luck.
What is the issue here? I think monitor or any other classes have handles created run time, however interface still should be static time compiled and all elements of interface should be available.
You cannot use a variable inside the []'s to reference the task.
A generate loop gets flattened out at elaboration before simulation starts. So you would need to call the task with
vif.g1[0].ab;
Here is a complete self contained example. Please try posting your questions with the same.
interface itf;
for (genvar ii = 0; ii < 2; ii++) begin : g1
task ab ();
$display("%m");
endtask // ab
end : g1
endinterface : itf
module top;
itf i1();
virtual itf vif;
initial begin
vif = i1;
vif.g1[0].ab;
end
endmodule : top