UVM error when using multiple sequencers using for loop construct - system-verilog

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.

Related

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

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

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).

How to slicing array interface in system verliog

I try to use array interface mapping through always_comb procedure instead of generate a statement.
You can see my test codes is in below(https://www.edaplayground.com/x/5cLt)
interface tintf;
bit valid;
bit data;
bit stall;
endinterface: tintf
module top;
tintf intf_a[0:3]();
bit valid, data, stall;
always_comb begin
loop_for_mapping:
for(int i = 0; i < 4; i++) begin
intf_a[i].valid = valid;
intf_a[i].data = data;
end
end
endmodule
But I see the error message below.
intf_a[i].valid = valid;
| ncelab: *E,NOTPAR (./testbench.sv,18|13): Illegal operand for constant expression [4(IEEE)].
I don't know that is why illegal...As I know 'i' in for-loop is considered constant.
Would you let me know what I missing??
It may be true for synthesis the for loop gets unrolled into a constant set of iterations, but it's not a constant from a language point of view. Simulation tools don't know which portions of your code you plan to synthesize. You need to write this as a generate-for loop.
for(genvar i = 0; i < 4; i++) begin : loop_for_mapping
always_comb begin
intf_a[i].valid = valid;
intf_a[i].data = data;
end
end
instantiating an interface (or a module) as an array (arrayed instance) is a part of the generate feature of verilog. As a result, you can only access those by using literal constants (0,1,2...) or from other generate blocks (see dave_59's answer).
It can be more flexible if you make arrays of the interface variables instead:
interface tintf;
bit [3:0] valid;
bit [3:0] data;
bit [3:0] stall;
endinterface: tintf
module top;
tintf intf_a();
bit valid, data, stall;
always_comb begin
loop_for_mapping:
for(int i = 0; i < 4; i++) begin
intf_a.valid[i] = valid;
intf_a.data[i]= data;
end
end
endmodule

How do I implement a Parametrizable Mux in SystemVerilog?

I am getting the following error in System Verilog with VCS synthesizer:
The following access has an invalid number of indices.
bus[i]
I am basically trying to do a parametrizable mux made of interfaces, with the select bus being one-hot:
module myMux
#(int unsigned WIDTH=3)
(
my_interface bus[WIDTH-1:0],
input logic [WIDTH-1:0] select,
output logic [31:0] out_data
)
always_comb begin
out_data = 'x;
for (int unsigned i=0; i < WIDTH; i++) begin
if (select[i]) out_data = bus[i].in_data;
end
end
endmodule
I tried the different methods outlined in this answer here (including using |= ) but I always get the same error. Using a "genvar i" instead of an int does not even compile.
If I replace bus[i] with bus[0], then it compiles (but it is not what I want) . . . also, replacing WIDTH with a number in the for statement (ie i < 1) gives me the same error even though it is less than the value of WIDTH.
Any ideas? the code needs to be synthesizable.
Accessing an instances of an arrayed interface can only be accessed via a simulation constant (parameter, genvar, or hard-coded number). Data types and design elements both use dotted names to access there respected member or hierarchical-name, but the rules for accessing index arrays are different. Best description I can quickly find is in IEEE Std 1800-2012 § 23.6 Hierarchical names and § 23.7 Member selects and hierarchical names.
Here are two possible solutions:
Tri-state solution: floating if select is 0, x on the bit with multiple conflicting drivers.
module myMux
#(int unsigned WIDTH=3)
(
my_interface bus[WIDTH-1:0],
input logic [WIDTH-1:0] select,
output wire [31:0] out_data
);
for (genvar i=0; i < WIDTH; i++) begin : loop
assign out_data = select[i] ? bus[i].in_data : 'z;
end
endmodule
Priority selector: using a local 2D array to map the interface instances. This map can be accessed in an always block. If you are on FPGA, this is the better solution as it doesn't need tri-state.
module myMux
#(int unsigned WIDTH=3)
(
my_interface bus[WIDTH-1:0],
input logic [WIDTH-1:0] select,
output logic [31:0] out_data
);
logic [31:0] map [WIDTH];
for (genvar i=0; i < WIDTH; i++) begin : loop
assign map[i] = bus[i].in_data;
end
always_comb begin
out_data = 'x;
for(int unsigned i=0; i<WIDTH; i++) begin
if (select[i]) out_data = map[i];
end
end
endmodule

Is it possible to append the value of macro argument instead of appending it literally?

I have a macro that I would like to use in the following fashion:
`define assign_m(CH, INT_NUM) \
assign dp_input``CH``_if.master_mp.sigA[INT_NUM] = some_signal[CH][INT_NUM];
generate
for(genvar i=0; i<2; i++) begin
for(genvar j=0; j<2; j++) begin
`assign_m(i,j)
end
end
endgenerate
I would like to have a construct like this to expand to:
assign dp_input0_if.master_mp.sigA[0] = some_signal[0][0];
assign dp_input0_if.master_mp.sigA[1] = some_signal[0][1];
assign dp_input1_if.master_mp.sigA[0] = some_signal[1][0];
assign dp_input1_if.master_mp.sigA[1] = some_signal[1][1];
But of course it doesn't happen that way as Verilog literally appends the variable j instead of its value (§ 22.5 `define, `undef, and `undefineall of IEEE Std 1800-2012, page 644).
How can I have a macro where the argument's value is appended?
If dp_input_if can be created as an array and an indexed version used, then it could be written out as:
integer i;
integer j;
always #* begin
for(i=0; i<2; i++) begin
for(j=0; j<2; j++) begin
dp_input_if[i].master_mp.sigA[j] = some_signal[i][j];
end
end
end