fork join within for loop in system verilog - 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).

Related

System Verilog: randomization per instance at initial

I want to simulate a multiple latches with random starting conditions, but I want each instance to have its own initial condition
This is a simplified version of the code. I would like the value to be different in both of the instances, without changing the interface
module random_usage();
integer addr1;
real data;
initial begin
addr1 = $urandom();
data = $urandom();
$display("addr1=%0d, data=%0d",addr1,data);
end
endmodule
module tb();
integer seed = 1;
random_usage a();
random_usage b();
initial
begin
#5;
seed = $get_initial_random_seed();
$display("seed=%0d", seed);
end
endmodule
What I have seen so far:
Instance specific $urandom in system-verilog
solution doesn't work in initial condition, or even when you feed the same clock
https://www.systemverilog.io/randomization
I have modules, so i don't know how to apply the solutions, or even if it will work here
https://www.reddit.com/r/FPGA/comments/jd0dmu/system_verilog_force_randomization_different_per/
seems to be the same question, and there is no straight solution, but the last person gave a VCS flag. I am using VCS, but i have not been able to get the flag to work
The IEEE 1800-2017 SystemVerilog LRM section 18.14.1 Random stability properties says rather naively that each instances gets seeded with the same initialization seed.
Most tools now have a switch changing that behavior by using the hierarchical path name to seed each instance. Some tools have even made that the default. If you want tool independent behavior, you can use this package:
package seed_instance;
int initial_seed = $urandom;
function automatic void srandom(string path);
static int hash[int];
int hash_value = initial_seed;
process p = process::self();
for(int i=0;i<path.len();i++)
hash_value+=path[i]*(i*7);
if (!hash.exists(hash_value))
hash[hash_value] = hash_value;
else
hash[hash_value]+=$urandom; // next seed
p.srandom(hash[hash_value]);
endfunction
endpackage
module random_usage();
integer addr1;
real data;
initial begin
seed_instance::srandom($sformatf("%m"));
addr1 = $urandom();
data = $urandom();
$display("1: addr1=%0d, data=%0d",addr1,data);
end
initial begin
seed_instance::srandom($sformatf("%m"));
addr1 = $urandom();
data = $urandom();
$display("2: addr1=%0d, data=%0d",addr1,data);
end
endmodule
module tb();
integer seed = 1;
random_usage a();
random_usage b();
endmodule

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 make randomize redo until get the number I want?

A basic sequence class for randomize reset below:
class random_reset;
rand int rst_period;
constraint rst_range { rst_period inside {[1:100]}; }
task random_system_reset (
ref reg rst,
ref reg clk);
begin
rst = 1;
repeat (rst_period) #(posedge clk);
rst = 0;
end
endtask
endclass
However, I test need at least 6 clocks. Got anyway to make sure when I call this class, will get the random value bigger than 6?
Assuming you want to keep your original constraint as-is, but in some circumstances, you need to enforce the period to be 6 or more, you can use randomize() with:
random_reset rr = new();
initial rr.randomize() with { rst_period >= 6; };
For this rr object, rst_period will be between 6 and 100.
Refer to IEEE Std 1800-2017, section 18.7 In-line constraints—randomize() with.

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