"A variable index into the for generate block is illegal" error - verilog

I created a group tasks using "generate" and was trying to call them using a for loop.
I have used generate block to create multiple instances of module, so I thought I could use it similarly to create multiple tasks. I plan to have no arguments for the tasks, and always call the tasks in a loop. But the number of tasks to be called is dependent on a parameter (instead of "4" in the example below, it will be a parameter).
Here is an example of what I was trying to do:
task display;
fork
other_tasks();
for(int i=0; i<4; i++) begin
create_task[i].print_i;
end
// create_task[0].print_i;
// create_task[1].print_i;
// create_task[2].print_i;
// create_task[3].print_i;
join
endtask
generate
for(genvar i=0; i<4; i++) begin : create_task
task print_i();
$display("%t, %m, Message #%0d", $time, i);
#1;
endtask
end : create_task
endgenerate
The code gives me no error if I call the tasks separately (commented lines) instead of using "for". However, I would prefer to do this using a loop because the number of tasks I need to call may vary. What would be the correct way of doing this?

Based on the the limited requirements you provided, the simplest solution is triggering a single event that calls all the tasks.
event print;
task display;
fork
other_tasks();
->print;
join
endtask
generate
for(genvar i=0; i<4; i++) begin : create_task
task print_i();
$display("%t, %m, Message #%0d", $time, i);
#1;
endtask
always #print print_i;
end : create_task
endgenerate
If you want to only call some of the tasks, you could create an array of events, and trigger the array indexed by a variable.
event print[4];
task display;
fork
other_tasks();
->print[some_index];
join
endtask
generate
for(genvar i=0; i<4; i++) begin : create_task
task print_i();
$display("%t, %m, Message #%0d", $time, i);
#1;
endtask
always #print[i] print_i;
end : create_task
endgenerate
If you ever need to pass arguments to the task, there are ways of using interface classes instead of using events.
interface class abstract_print;
pure virtual task printer(input int arg);
endclass
abstract_print list[4];
task display;
fork
other_tasks();
list[some_index].print(some_arg);
join
endtask
for(genvar i=0; i<4; i++) begin : create_task
task print_i(int arg);
$display("%t, %m, Message #%0d", $time, i);
#1;
endtask
class concrete_print implements abstract_print;
function new;
list[i] = this;
endfunction
task print(int arg);
print_i(arg);
endtask
endclass
concrete_print p = new;
end : create_task

Generate loops are for structural design.
Move the loop inside the called task and use for. Pass an arg to the task which determines the number of times to do something.
module tb ();
task display;
fork
other_tasks();
for(int i=0; i<4; i++) begin
print_i(3);
end
join
endtask
task other_tasks();
endtask
task print_i(input int i);
for(int j = 0; j < i ; j++)
$display("time = %0t, count = #%0d", $time, j);
#1;
endtask
initial begin
display();
end
endmodule
Produces:
time = 0, count = #0
time = 0, count = #1
time = 0, count = #2
time = 1, count = #0
time = 1, count = #1
time = 1, count = #2
time = 2, count = #0
time = 2, count = #1
time = 2, count = #2
time = 3, count = #0
time = 3, count = #1
time = 3, count = #2

Related

Interconnect modules in for loop with different connectivity on some ports between different instances

Is it possible to instantiate a module in a for loop and change some port connectivity between different instances using an if statement?
Something like:
genvar g_i;
generate
for (g_i = 0; g_i < PWLINK_NUM; g_i++) begin : pwrlink
pdndlvrlnlpwrlinkvert_cbb pdndlvrlnlpwrlink_cbb(
if (g_i == 0) begin
.dftmeasen(pwr_dftmeasen),
end
else begin
.dftmeasen(1'b0),
end
.bldr_en(bldr_en[g_i]),
.bldr_mode(bldr_mode[g_i]),
.bldr_strength(bldr_strength[g_i]),
.hven(hvenout[g_i]),
.legacy_mode_en(legacy_mode_en[g_i]),
.force_on(force_on[g_i]),
.pwrup(pwrup[g_i]),
end
endgenerate
I get a compile error when I try to use if-else inside a generate for loop that way.
One way to do this is to declare a new signal and connect it to the instance port just like all the other ports:
wire [PWLINK_NUM-1:0] dftmeasen = { {(PWLINK_NUM-1){1'b0}}, pwr_dftmeasen};
genvar g_i;
generate
for (g_i = 0; g_i < PWLINK_NUM; g_i++) begin : pwrlink
pdndlvrlnlpwrlinkvert_cbb pdndlvrlnlpwrlink_cbb (
.dftmeasen (dftmeasen[g_i]),
.bldr_en (bldr_en [g_i]),
One possibilty is to define a temp signal for dftmeasen differently for differnt iterations, like int the following. tmp is defined as a wire for iteration 0 and as a parameter for the rest. The only secret is to have named blocks in the if statement with the same name in both paths. I used i for the name.
for (genvar g_i = 0; g_i < PWLINK_NUM; g_i++) begin : pwrlink
if (g_i == 0) begin: i // <<
wire tmp = pwr_dftmeasen;
end: i
else begin: i // <<
parameter bit tmp = 0;
end: i
pdndlvrlnlpwrlinkvert_cbb pdndlvrlnlpwrlink_cbb(
.dftmeasen(i.tmp), // i.tmp
.bldr_en(bldr_en[g_i])
);
end

Getting an error calling a task in verilog testbench

I've following tasks with different input vectors (that part is not written for simplicity) in my Verilog testbench
task input_set_1;
begin
end
endtask
task input_set_2;
begin
end
endtask
task input_set_3;
begin
end
endtask
task input_set_4;
begin
end
endtask
I'm trying to call only one of them during the testbench simulation using the below task
task input_calling;
integer i;
begin
i = $urandom_range(1, 4);
input_set_i;
end
endtask
initial begin
#3;
input_calling;
end
I'm expecting with each test run, any of the input_set_1, or input_set_2 or input_set_3 or input_set_4 will be called based on the random number i=1 to 4, however, I'm getting an error saying input_set_i is not defined. I suspect some string to int mismatch is preventing I to take randomized value in the input _set_i task.
You are trying to do something which is impossible in verilog (as well as in any non-scripting language). Name of the task is a compile-time element which must be known to the compiler at compilation time. i is a run-time, dynamic element. You cannot use run-time objects to modify compile-time objects.
So, your best bet is to use dynamic language features to make your program work, e.g. if statements. Here is an example:
module top;
task a;
$display("a");
endtask
task b;
$display("b");
endtask
initial begin
for (int k = 0; k < 4; k++) begin
automatic int i = $urandom_range(2,1);
if (i == 1)
a;
else if (i == 2)
b;
end
end
endmodule

How to implement transition coverage on non-consecutive sampling points?

var_1 changes from value 0 to 1 then from 1 to 2 and so on till 15, but not on consecutive sampling points. I sample on every clock cycle, but the value might change after some arbitrary clk cycles. The transition coverage I write does not work. Can we write transition coverage for this case?
bit [3:0] var_1;
var1: coverpoint var_1
{
bins var_1_trans_bin = (0=>1=>2=>3=>4=>5=>6=>7=>8=>9=>10=>11=>12=>13=>14=>15);
bins var_1_bin[] = {[0:15]};
}
I see that the var_1_bin is getting covered 100% but not the var_1_trans_bin.
Here is the whole code:
module GRG_coverpoint;
bit [3:0] var_1;
bit Running;
bit clk;
// Example showing bins and transitions
covergroup CG1 #(posedge clk);
coverpoint var_1
{
bins var_1_trans_bin = (0=>1=>2=>3=>4=>5=>6=>7=>8=>9=>10=>11=>12=>13=>14=>15);
bins var_1_bin[] = {[0:15]};
}
endgroup
initial begin
automatic CG1 cg1_inst = new;
for (int j = 0; j < 16; j++)
begin
var_1 = j;
#20;
end
$display ("CG1 Coverage = %.2f%%", cg1_inst.get_coverage());
Running = 0;
end
initial begin
clk = 0;
Running = 1;
while (Running) begin
#5 clk = ~clk;
end
$display ("Finished!!");
end
endmodule
As you realized, you do not want to sample coverage on every clock cycle. You want to sample it only when var_1 changes value. You can declare the covergroup without the optional coverage_event (#(posedge clk) in your case), then call the sample method in a separate procedural block every time the variable changes:
module GRG_coverpoint;
bit [3:0] var_1;
bit [3:0] var_2;
bit Running;
bit clk;
// Example showing bins and transitions
covergroup CG1;
coverpoint var_1
{
bins var_1_trans_bin = (0=>1=>2=>3=>4=>5=>6=>7=>8=>9=>10=>11=>12=>13=>14=>15);
bins var_1_bin[] = {[0:15]};
}
endgroup
CG1 cg1_inst = new;
initial begin
cg1_inst.sample(); // Sample the initial 0 value
forever #(var_1) cg1_inst.sample();
end
initial begin
for (int j = 0; j < 16; j++)
begin
var_1 = j;
#20;
end
$display ("CG1 Coverage = %.2f%%", cg1_inst.get_coverage());
Running = 0;
end
initial begin
clk = 0;
Running = 1;
while (Running) begin
#5 clk = ~clk;
end
$display ("Finished!!");
end
endmodule

How to access Verilog genvar generated instances and their signals

I need to initialize arrays in generated instances via generate block in Verilog. I'm trying to use the syntax below, however, I get an error as
External reference foo[0].bar.array[0] remains unresolved.
in Xilinx ISE.
integer i, j;
initial begin
// initialization of arrays
for(i=0; i<(2**7); i=i+1) begin
valid_array[i] = 0;
for(j=0; j<N; j=j+1) begin
foo[j].bar.array[i] = 0;
end
end
end
This is my generate block:
genvar jj;
generate
for(jj = 0; jj < N; jj = jj + 1) begin : foo
Memory_v3
#(
.ADDRESS_WIDTH(INDEX),
.DATA_WIDTH(TAG)
) bar
(
.clock(clock),
.we(we),
.addr_a(addra),
.addr_b(addrb),
.din(din),
.dout(dout)
);
end
endgenerate
What is the correct way to do this?
the problem is that foo block gets resolved at compile time. So, the names like foo[0], foo[1] are pre-generated and are compile-time names, they not work with dynamic index resolution at run-time. Therefore foo[j] will not work.
the solution in this case is to use another generate block to initialize them. Something like the following.
generate
for(jj = 0; jj < N; jj = jj + 1) begin : foo_init
initial begin
// initialization of arrays
for(i=0; i<(2**7); i=i+1) begin
foo[jj].bar.array[i] = 0;
end
end
end
endgenerate
Same story is with array of instances.
FOO bar[3:0](); // array of instances
int i,j;
generate
for(jj = 0; jj < 4; jj = jj + 1) begin : foo_init
initial begin
for(i=0; i<(2**7); i=i+1) begin
bar[jj].array[i] = 0;
end
end
end
endgenerate
BTW, in system verilog you can avoid using generate/endgenerate pairs:

Two genvar in single genarte for loop?

Is below sort of generate loop is valid in system verilog.
genvar i,j;
for (i=0,j=5; i<5 && j<10; i++,j++) begin:M1
integer t;
initial begin
t = i*j;
end
endgenerate
Nope. I'm not even sure what the behavior of this code should be, or in what order they execute.
genvar i,j;
for (i = 0; i <5; i++) begin
for (j = 5; j < 10; j++) begin
$display("%d", i*j);
end
end
you don't need 'endgenerate' in the end.
You cannot do much with genvars in your example.
The following code mimics what you asked for, but it might not work with all simulators. It works with synopsys vcs but does not work with icarus.
module g;
genvar i;
for (i=0; i<5; i++) begin:M
if (i == 0) begin:B
parameter j = 5;
end
else begin:B
parameter j = g.M[i-1].B.j + 1;
end
integer t;
initial begin
t = i*B.j;
$display(i, B.j, t);
end
end // block: M
endmodule // g
the idea is to declare the parameter j inside the generate loop so that its value is an increment of the value declared in the previous iteration. You need to add a named block 'B' to declare conditionally.

Resources