Tests: Add test cases for interface array access with loop variable index (#1418 tests) (#7011)

This commit is contained in:
Leela Pakanati 2026-02-07 09:06:37 -06:00 committed by GitHub
parent 6100c39764
commit 8922794088
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 466 additions and 0 deletions

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test interface array access with loop variables
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(fails=True)
test.passes()

View File

@ -0,0 +1,371 @@
// DESCRIPTION: Verilator: Test interface array access with loop variables
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Leela Pakanati
// SPDX-License-Identifier: CC0-1.0
interface simple_if;
logic data;
logic [7:0] value;
modport source (output data, output value);
modport sink (input data, input value);
endinterface
// Test 6: Submodule with interface array as sink modport port
module sub_sink (
simple_if.sink ifaces [3:0],
output logic [7:0] sum_out
);
always_comb begin
sum_out = 8'b0;
for (int i = 0; i < 4; i++) begin
sum_out = sum_out + ifaces[i].value;
end
end
endmodule
// Test 7: Submodule with interface array as source modport port
module sub_source (
simple_if.source ifaces [3:0]
);
always_comb begin
for (int i = 0; i < 4; i++) begin
ifaces[i].data = 1'b1;
ifaces[i].value = 8'(i + 100);
end
end
endmodule
// Test 8: Submodule with interface array without modport
module sub_generic (
simple_if ifaces [3:0]
);
always_comb begin
for (int i = 0; i < 4; i++) begin
ifaces[i].data = 1'b0;
ifaces[i].value = 8'(i + 50);
end
end
endmodule
// Test 11: Parameterized submodule with interface array size from parameter
module l1_param_sub #(
parameter NUM_IFACES = 4
) (
simple_if.sink l0_ifaces [NUM_IFACES-1:0],
output logic [7:0] l0_sum
);
always_comb begin
l0_sum = 8'b0;
for (int i = 0; i < NUM_IFACES; i++) begin
l0_sum = l0_sum + l0_ifaces[i].value;
end
end
endmodule
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
localparam N = 4;
simple_if ifaces [N-1:0] ();
// Test 1: Simple for loop writing to interface array
always_comb begin
for (int i = 0; i < N; i++) begin
ifaces[i].data = '0;
end
end
// Test 2: For loop reading from interface array
logic [N-1:0] read_data;
always_comb begin
for (int j = 0; j < N; j++) begin
read_data[j] = ifaces[j].data;
end
end
// Test 3: For loop with expression in body
always_comb begin
for (int k = 0; k < N-1; k++) begin
// Use k to index, accessing different member
ifaces[k].value = 8'(k);
end
ifaces[N-1].value = 8'hFF;
end
// Test 4: Descending loop with >= comparison
logic [N-1:0] desc_data;
always_comb begin
for (int p = N-1; p >= 0; p--) begin
desc_data[p] = ifaces[p].data;
end
end
// Test 4b: Ascending loop with <= comparison
logic [N-1:0] lte_data;
always_comb begin
for (int m = 0; m <= N-1; m++) begin
lte_data[m] = ifaces[m].data;
end
end
// Test 4c: Descending loop with > comparison
simple_if gt_ifaces [N-1:0] ();
always_comb begin
for (int n = N; n > 0; n--) begin
gt_ifaces[n-1].data = 1'b1;
gt_ifaces[n-1].value = 8'(n);
end
end
// Test 5: Multiple interface arrays in same loop
simple_if other_ifaces [N-1:0] ();
always_comb begin
for (int q = 0; q < N; q++) begin
other_ifaces[q].data = ifaces[q].data;
other_ifaces[q].value = ifaces[q].value;
end
end
// Test 6: Interface array as sink modport port (read from ports in loop)
logic [7:0] sink_sum;
sub_sink u_sub_sink (.ifaces(ifaces), .sum_out(sink_sum));
// Test 7: Interface array as source modport port (write to ports in loop)
simple_if source_ifaces [N-1:0] ();
sub_source u_sub_source (.ifaces(source_ifaces));
// Test 8: Interface array without modport (read/write in loop)
simple_if generic_ifaces [N-1:0] ();
sub_generic u_sub_generic (.ifaces(generic_ifaces));
// Test 9: Ascending loop with step of 2
simple_if step2_ifaces [7:0] ();
always_comb begin
for (int i = 0; i < 8; i += 2) begin
step2_ifaces[i].data = 1'b1;
step2_ifaces[i].value = 8'(i);
end
for (int i = 1; i < 8; i += 2) begin
step2_ifaces[i].data = 1'b0;
step2_ifaces[i].value = 8'hAA;
end
end
// Test 10: Descending loop with step of -2
simple_if step2d_ifaces [7:0] ();
always_comb begin
for (int i = 6; i >= 0; i -= 2) begin
step2d_ifaces[i].data = 1'b1;
step2d_ifaces[i].value = 8'(i);
end
for (int i = 7; i >= 1; i -= 2) begin
step2d_ifaces[i].data = 1'b0;
step2d_ifaces[i].value = 8'hBB;
end
end
// Test 11: Parameterized submodule with interface array size from parameter
// Override NUM_IFACES from default 4 to 6 to test that pre-unroll
// uses the overridden parameter value (not the default)
simple_if l0_param_ifaces [5:0] ();
always_comb begin
for (int i = 0; i < 6; i++) begin
l0_param_ifaces[i].data = 1'b1;
l0_param_ifaces[i].value = 8'(i * 10);
end
end
logic [7:0] l0_param_sum;
l1_param_sub #(.NUM_IFACES(6)) u_l1_param (
.l0_ifaces(l0_param_ifaces),
.l0_sum(l0_param_sum)
);
// Test 12: Complex index expression (N-1-i) - reversed assignment
simple_if rev_ifaces [N-1:0] ();
always_comb begin
for (int i = 0; i < N; i++) begin
rev_ifaces[N-1-i].data = 1'b1;
rev_ifaces[N-1-i].value = 8'(i);
end
end
// Test 13: Non-zero-based interface array range [2:5]
simple_if nzb_ifaces [2:5] ();
always_comb begin
for (int i = 2; i <= 5; i++) begin
nzb_ifaces[i].data = 1'b1;
nzb_ifaces[i].value = 8'(i);
end
end
// Verification
int cycle;
initial cycle = 0;
always @(posedge clk) begin
cycle <= cycle + 1;
// Check that interfaces were properly accessed
if (cycle == 1) begin
// After initialization, data should be 0
for (int i = 0; i < N; i++) begin
if (ifaces[i].data !== 1'b0) begin
$display("%%Error: ifaces[%0d].data should be 0, got %b", i, ifaces[i].data);
$stop;
end
end
// Check values
for (int i = 0; i < N-1; i++) begin
if (ifaces[i].value !== 8'(i)) begin
$display("%%Error: ifaces[%0d].value should be %0d, got %0d", i, i, ifaces[i].value);
$stop;
end
end
if (ifaces[N-1].value !== 8'hFF) begin
$display("%%Error: ifaces[%0d].value should be 0xFF, got %0d", N-1, ifaces[N-1].value);
$stop;
end
// Test 4b: Check lte_data (uses <= comparison)
for (int i = 0; i < N; i++) begin
if (lte_data[i] !== 1'b0) begin
$display("%%Error: lte_data[%0d] should be 0, got %b", i, lte_data[i]);
$stop;
end
end
// Test 4c: Check gt_ifaces (uses > comparison, values are 1,2,3,4)
for (int i = 0; i < N; i++) begin
if (gt_ifaces[i].data !== 1'b1) begin
$display("%%Error: gt_ifaces[%0d].data should be 1, got %b", i, gt_ifaces[i].data);
$stop;
end
if (gt_ifaces[i].value !== 8'(i + 1)) begin
$display("%%Error: gt_ifaces[%0d].value should be %0d, got %0d", i, i+1, gt_ifaces[i].value);
$stop;
end
end
// Test 6: Check sink_sum (sum of ifaces[0..3].value = 0+1+2+255 = 258 = 0x02)
if (sink_sum !== 8'h02) begin
$display("%%Error: sink_sum should be 0x02, got 0x%02x", sink_sum);
$stop;
end
// Test 7: Check source_ifaces (written by sub_source with i+100)
for (int i = 0; i < N; i++) begin
if (source_ifaces[i].data !== 1'b1) begin
$display("%%Error: source_ifaces[%0d].data should be 1, got %b", i, source_ifaces[i].data);
$stop;
end
if (source_ifaces[i].value !== 8'(i + 100)) begin
$display("%%Error: source_ifaces[%0d].value should be %0d, got %0d", i, i+100, source_ifaces[i].value);
$stop;
end
end
// Test 8: Check generic_ifaces (written by sub_generic with i+50)
for (int i = 0; i < N; i++) begin
if (generic_ifaces[i].data !== 1'b0) begin
$display("%%Error: generic_ifaces[%0d].data should be 0, got %b", i, generic_ifaces[i].data);
$stop;
end
if (generic_ifaces[i].value !== 8'(i + 50)) begin
$display("%%Error: generic_ifaces[%0d].value should be %0d, got %0d", i, i+50, generic_ifaces[i].value);
$stop;
end
end
// Test 9: Check step2_ifaces (even indices have i, odd have 0xAA)
for (int i = 0; i < 8; i++) begin
if (i % 2 == 0) begin
if (step2_ifaces[i].data !== 1'b1) begin
$display("%%Error: step2_ifaces[%0d].data should be 1, got %b", i, step2_ifaces[i].data);
$stop;
end
if (step2_ifaces[i].value !== 8'(i)) begin
$display("%%Error: step2_ifaces[%0d].value should be %0d, got %0d", i, i, step2_ifaces[i].value);
$stop;
end
end else begin
if (step2_ifaces[i].data !== 1'b0) begin
$display("%%Error: step2_ifaces[%0d].data should be 0, got %b", i, step2_ifaces[i].data);
$stop;
end
if (step2_ifaces[i].value !== 8'hAA) begin
$display("%%Error: step2_ifaces[%0d].value should be 0xAA, got 0x%02x", i, step2_ifaces[i].value);
$stop;
end
end
end
// Test 10: Check step2d_ifaces (even indices descending, odd indices 0xBB)
for (int i = 0; i < 8; i++) begin
if (i % 2 == 0) begin
if (step2d_ifaces[i].data !== 1'b1) begin
$display("%%Error: step2d_ifaces[%0d].data should be 1, got %b", i, step2d_ifaces[i].data);
$stop;
end
if (step2d_ifaces[i].value !== 8'(i)) begin
$display("%%Error: step2d_ifaces[%0d].value should be %0d, got %0d", i, i, step2d_ifaces[i].value);
$stop;
end
end else begin
if (step2d_ifaces[i].data !== 1'b0) begin
$display("%%Error: step2d_ifaces[%0d].data should be 0, got %b", i, step2d_ifaces[i].data);
$stop;
end
if (step2d_ifaces[i].value !== 8'hBB) begin
$display("%%Error: step2d_ifaces[%0d].value should be 0xBB, got 0x%02x", i, step2d_ifaces[i].value);
$stop;
end
end
end
// Test 11: Check l0_param_sum (0*10 + 1*10 + 2*10 + 3*10 + 4*10 + 5*10 = 150)
if (l0_param_sum !== 8'd150) begin
$display("%%Error: l0_param_sum should be 150, got %0d", l0_param_sum);
$stop;
end
// Test 12: Check rev_ifaces (reversed: rev_ifaces[N-1-i].value = i)
// So rev_ifaces[3].value=0, rev_ifaces[2].value=1, rev_ifaces[1].value=2, rev_ifaces[0].value=3
for (int i = 0; i < N; i++) begin
if (rev_ifaces[i].data !== 1'b1) begin
$display("%%Error: rev_ifaces[%0d].data should be 1, got %b", i, rev_ifaces[i].data);
$stop;
end
if (rev_ifaces[i].value !== 8'(N - 1 - i)) begin
$display("%%Error: rev_ifaces[%0d].value should be %0d, got %0d", i, N-1-i, rev_ifaces[i].value);
$stop;
end
end
// Test 13: Check nzb_ifaces (non-zero-based [2:5], nzb_ifaces[i].value = i)
for (int i = 2; i <= 5; i++) begin
if (nzb_ifaces[i].data !== 1'b1) begin
$display("%%Error: nzb_ifaces[%0d].data should be 1, got %b", i, nzb_ifaces[i].data);
$stop;
end
if (nzb_ifaces[i].value !== 8'(i)) begin
$display("%%Error: nzb_ifaces[%0d].value should be %0d, got %0d", i, i, nzb_ifaces[i].value);
$stop;
end
end
end
if (cycle == 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,10 @@
%Error: t/t_interface_array_loop_bad.v:27:14: Expecting expression to be constant, but variable isn't const: 'i'
: ... note: In instance 't'
27 | ifaces[i].value = 8'(i);
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_interface_array_loop_bad.v:27:13: Could not expand constant selection inside dotted reference: 'i'
: ... note: In instance 't'
27 | ifaces[i].value = 8'(i);
| ^
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test non-constant interface array loop index error
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,36 @@
// DESCRIPTION: Verilator: Test non-constant interface array loop index error
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Leela Pakanati
// SPDX-License-Identifier: CC0-1.0
interface simple_bad_if;
logic [7:0] value;
endinterface
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
localparam N = 4;
simple_bad_if ifaces [N-1:0] ();
// BAD: Loop with runtime-variable bound - not unrollable, so the
// interface array index cannot be resolved to a constant.
logic [7:0] limit;
always_comb begin
for (int i = 0; i < limit; i++) begin
ifaces[i].value = 8'(i);
end
end
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test interface array access with loop variables (no inline)
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.top_filename = "t/t_interface_array_loop.v"
test.compile(fails=True, v_flags2=["-fno-inline"])
test.passes()