verilator/test_regress/t/t_interface_param_dependency.v

301 lines
8.4 KiB
Systemverilog

// DESCRIPTION: Test interface parameter dependency resolution
//
// Test that interface/modport parameters can be accessed when the
// interface/modport is an IO port of the module.
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2025 by Paul Swirhun
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checkd(gotv,
expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
interface TEST_IF #(
parameter int FOO = 1,
parameter int BAR = FOO * 10
);
logic [31:0] data;
modport mp(input data);
endinterface
module submod_iface (
output logic [31:0] result,
TEST_IF iface
);
assign result = iface.FOO + iface.BAR;
endmodule
module submod_modport (
output logic [31:0] result,
TEST_IF.mp mp
);
assign result = mp.FOO + mp.BAR;
endmodule
module submod_assert2 #(
parameter int EXPECTED_FOO = 0,
parameter int EXPECTED_BAR = 0
) (
TEST_IF iface
);
initial begin
// Verify the dependent parameter BAR is correctly computed in the module
if (iface.FOO != EXPECTED_FOO) begin
$error("FOO mismatch in module: expected %0d, got %0d", EXPECTED_FOO, iface.FOO);
end
if (iface.BAR != EXPECTED_BAR) begin
$error("BAR dependency failed in module: expected %0d, got %0d", EXPECTED_BAR, iface.BAR);
end
end
endmodule
// Test module that asserts interface parameter values - catches dependency bugs
module submod_assert #(
parameter int EXPECTED_FOO = 0,
parameter int EXPECTED_BAR = 0
) (
TEST_IF iface
);
// Make a new interface with inherited parameters and pass it to a submodule
// with inherited expectations.
TEST_IF #(
.FOO(iface.FOO),
.BAR(iface.BAR)
) iface2 ();
// Test mixed parameters: constant + interface port reference
TEST_IF #(
.FOO(7), // Constant parameter
.BAR(iface.FOO) // References interface port
) iface_mixed ();
// Test specifying only FOO parameter (BAR should use default calculation)
TEST_IF #(
.FOO(iface.FOO) // Only FOO specified, BAR = FOO * 10
) iface_foo_only ();
// Test specifying only BAR parameter (FOO should use default)
TEST_IF #(
.BAR(iface.BAR) // Only BAR specified, FOO = default (1)
) iface_bar_only ();
// Test no parameters specified (both should use defaults)
TEST_IF iface_defaults ();
submod_assert2 #(
.EXPECTED_FOO(EXPECTED_FOO),
.EXPECTED_BAR(EXPECTED_BAR)
) u_submod_assert2 (
.iface(iface2)
);
// Test the mixed parameter interface
submod_assert2 #(
.EXPECTED_FOO(7),
.EXPECTED_BAR(EXPECTED_FOO) // BAR should get iface.FOO value
) u_mixed_assert (
.iface(iface_mixed)
);
// Test FOO-only interface
submod_assert2 #(
.EXPECTED_FOO(EXPECTED_FOO),
.EXPECTED_BAR(EXPECTED_FOO * 10) // BAR = FOO * 10
) u_foo_only_assert (
.iface(iface_foo_only)
);
// Test BAR-only interface
submod_assert2 #(
.EXPECTED_FOO(1), // FOO = default
.EXPECTED_BAR(EXPECTED_BAR) // BAR = specified value
) u_bar_only_assert (
.iface(iface_bar_only)
);
// Test defaults interface
submod_assert2 #(
.EXPECTED_FOO(1), // FOO = default
.EXPECTED_BAR(10) // BAR = FOO * 10 = 1 * 10
) u_defaults_assert (
.iface(iface_defaults)
);
initial begin
// Verify the dependent parameter BAR is correctly computed in the module
if (iface.FOO != EXPECTED_FOO) begin
$error("FOO mismatch in module: expected %0d, got %0d", EXPECTED_FOO, iface.FOO);
end
if (iface.BAR != EXPECTED_BAR) begin
$error("BAR dependency failed in module: expected %0d, got %0d", EXPECTED_BAR, iface.BAR);
end
// Verify all interface instances have correct parameter values
if (iface2.FOO != EXPECTED_FOO) begin
$error("iface2.FOO mismatch: expected %0d, got %0d", EXPECTED_FOO, iface2.FOO);
end
if (iface2.BAR != EXPECTED_BAR) begin
$error("iface2.BAR mismatch: expected %0d, got %0d", EXPECTED_BAR, iface2.BAR);
end
if (iface_mixed.FOO != 7) begin
$error("iface_mixed.FOO mismatch: expected 7, got %0d", iface_mixed.FOO);
end
if (iface_mixed.BAR != EXPECTED_FOO) begin
$error("iface_mixed.BAR mismatch: expected %0d, got %0d", EXPECTED_FOO, iface_mixed.BAR);
end
if (iface_foo_only.FOO != EXPECTED_FOO) begin
$error("iface_foo_only.FOO mismatch: expected %0d, got %0d", EXPECTED_FOO,
iface_foo_only.FOO);
end
if (iface_foo_only.BAR != EXPECTED_FOO * 10) begin
$error("iface_foo_only.BAR mismatch: expected %0d, got %0d", EXPECTED_FOO * 10,
iface_foo_only.BAR);
end
if (iface_bar_only.FOO != 1) begin
$error("iface_bar_only.FOO mismatch: expected 1, got %0d", iface_bar_only.FOO);
end
if (iface_bar_only.BAR != EXPECTED_BAR) begin
$error("iface_bar_only.BAR mismatch: expected %0d, got %0d", EXPECTED_BAR,
iface_bar_only.BAR);
end
if (iface_defaults.FOO != 1) begin
$error("iface_defaults.FOO mismatch: expected 1, got %0d", iface_defaults.FOO);
end
if (iface_defaults.BAR != 10) begin
$error("iface_defaults.BAR mismatch: expected 10, got %0d", iface_defaults.BAR);
end
end
endmodule
// Test parameterized interface chain: module parameter -> interface parameter -> submodule
module param_chain #(
parameter int TOP_PARAM = 3
) (
output logic [31:0] result
);
// Interface gets parameter from module parameter
TEST_IF #(.FOO(TOP_PARAM)) chain_iface ();
// Submodule uses interface (FOO=3, BAR should be 30)
submod_iface chain_sub (
.result(result),
.iface (chain_iface)
);
// Assert the chain works correctly
submod_assert #(
.EXPECTED_FOO(TOP_PARAM),
.EXPECTED_BAR(TOP_PARAM * 10)
) chain_assert (
.iface(chain_iface)
);
endmodule
module t;
// Test case 1: FOO specified, BAR should be FOO*10
TEST_IF #(.FOO(5)) tif_1 ();
// Test case 2: Both FOO and BAR specified explicitly
TEST_IF #(
.FOO(6),
.BAR(66)
) tif_2 ();
// Test case 3: Only BAR specified, FOO should be default
TEST_IF #(.BAR(77)) tif_3 ();
// Test case 4: Default parameters
TEST_IF tif_4 ();
logic [8:0][31:0] result;
// Test interface as port parameter
submod_iface u0 (
.result(result[0]),
.iface (tif_1)
);
submod_iface u1 (
.result(result[1]),
.iface (tif_2)
);
submod_iface u2 (
.result(result[2]),
.iface (tif_3)
);
submod_iface u3 (
.result(result[3]),
.iface (tif_4)
);
// Test modport as port parameter
submod_modport u4 (
.result(result[4]),
.mp(tif_1)
);
submod_modport u5 (
.result(result[5]),
.mp(tif_2)
);
submod_modport u6 (
.result(result[6]),
.mp(tif_3)
);
submod_modport u7 (
.result(result[7]),
.mp(tif_4)
);
// Test that interface parameter dependencies are correctly resolved in modules
submod_assert #(
.EXPECTED_FOO(5),
.EXPECTED_BAR(50)
) assert1 (
.iface(tif_1)
);
// Test parameterized interface chain: module param -> interface param -> submodule
param_chain #(.TOP_PARAM(4)) chain_test (.result(result[8]));
// Allow hierarchichal references to locally declared interfaces only when HIERPARAM is waived
/* verilator lint_off HIERPARAM */
TEST_IF #(.FOO(3)) test_if_local ();
logic [31:0] foo_local_1 = 32'(test_if_local.FOO);
logic [31:0] bar_local_1 = 32'(test_if_local.BAR);
localparam FOO_LOCAL = test_if_local.FOO;
localparam BAR_LOCAL = test_if_local.BAR;
logic [31:0] foo_local_2 = 32'(FOO_LOCAL);
logic [31:0] bar_local_2 = 32'(BAR_LOCAL);
/* verilator lint_on HIERPARAM */
initial begin
// Verify modules can access interface parameters correctly
`checkd(result[0], 55); // 5 + 50
`checkd(result[1], 72); // 6 + 66
`checkd(result[2], 78); // 1 + 77 (FOO default + BAR explicit)
`checkd(result[3], 11); // 1 + 10 (both defaults, BAR = FOO*10)
// Verify modport access gives same results
`checkd(result[4], 55); // 5 + 50
`checkd(result[5], 72); // 6 + 66
`checkd(result[6], 78); // 1 + 77
`checkd(result[7], 11); // 1 + 10
// Verify parameterized chain works
`checkd(result[8], 44); // 4 + 40 (TOP_PARAM=4, so FOO=4, BAR=40)
`checkd(foo_local_1, 3);
`checkd(bar_local_1, 30);
`checkd(foo_local_2, 3);
`checkd(bar_local_2, 30);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule