verilator/test_regress/t/t_interface_nested_port.v

373 lines
9.4 KiB
Systemverilog

// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Leela Pakanati
// SPDX-License-Identifier: CC0-1.0
// Issue #5066 - Combined test for nested interface ports with parameters
//
// Tests all parameter patterns in a 5-deep hierarchy:
// - Derived: W doubles at each level (L3=L4*2, L2A=L3*2, L1=L2*2)
// - Hard-coded: L2B.W=8 regardless of parent
// - Passthrough: L0A_W flows unchanged from top to L0A
// - Default: L0B uses default W=8
//
// With TOP_W=4, L0A_W=16:
// L4(W=4) -> L3(W=8) -> L2A(W=16) -> L1(W=32) -> L0A(W=16), L0B(W=8)
// -> L2B(W=8) -> L1(W=16) -> L0A(W=16), L0B(W=8)
interface l0_if #(
parameter int W = 8
);
logic [W-1:0] tb_in;
logic [W-1:0] dut_out;
endinterface
interface l1_if #(
parameter int W = 8,
parameter int L0A_W = 8
);
logic [W-1:0] tb_in;
logic [W-1:0] dut_out;
l0_if #(L0A_W) l0a (); // passthrough
l0_if l0b (); // default
endinterface
interface l2_if #(
parameter int W = 8,
parameter int L0A_W = 8
);
logic [W-1:0] tb_in;
logic [W-1:0] dut_out;
l1_if #(W * 2, L0A_W) l1 (); // derived
endinterface
interface l3_if #(
parameter int W = 8,
parameter int L0A_W = 8
);
logic [W-1:0] tb_in;
logic [W-1:0] dut_out;
l2_if #(W * 2, L0A_W) l2a (); // derived
l2_if #(8, L0A_W) l2b (); // hard-coded
endinterface
interface l4_if #(
parameter int W = 8,
parameter int L0A_W = 8
);
logic [W-1:0] tb_in;
logic [W-1:0] dut_out;
l3_if #(W * 2, L0A_W) l3 (); // derived
endinterface
// Handlers use unparameterized interface ports with parameterized output widths
module l0_handler #(
parameter int W = 8
) (
input logic clk,
l0_if l0,
output logic [W-1:0] dout
);
always_ff @(posedge clk) l0.dut_out <= l0.tb_in ^ W'('1);
assign dout = l0.dut_out;
endmodule
module l1_reader #(
parameter int W = 8
) (
l1_if l1,
output logic [W-1:0] dout
);
assign dout = l1.dut_out;
endmodule
module l1_driver #(
parameter int W = 8
) (
input logic clk,
l1_if l1
);
always_ff @(posedge clk) l1.dut_out <= l1.tb_in ^ W'('1);
endmodule
module l1_handler #(
parameter int W = 8,
parameter int L0A_W = 8
) (
input logic clk,
l1_if l1,
output logic [W-1:0] l1_dout,
output logic [L0A_W-1:0] l0a_dout,
output logic [7:0] l0b_dout
);
// Use reader/driver submodules instead of direct access
l1_reader #(W) m_rdr (
.l1(l1),
.dout(l1_dout)
);
l1_driver #(W) m_drv (
.clk(clk),
.l1(l1)
);
// Still instantiate l0_handlers for nested ports
l0_handler #(L0A_W) m_l0a (
.clk(clk),
.l0(l1.l0a),
.dout(l0a_dout)
);
l0_handler #(8) m_l0b (
.clk(clk),
.l0(l1.l0b),
.dout(l0b_dout)
);
endmodule
module l2_handler #(
parameter int W = 8,
parameter int L0A_W = 8
) (
input logic clk,
l2_if l2,
output logic [W-1:0] l2_dout,
output logic [W*2-1:0] l1_dout,
output logic [L0A_W-1:0] l0a_dout,
output logic [7:0] l0b_dout
);
always_ff @(posedge clk) l2.dut_out <= l2.tb_in ^ W'('1);
assign l2_dout = l2.dut_out;
l1_handler #(W * 2, L0A_W) m_l1 (
.clk(clk),
.l1(l2.l1),
.l1_dout(l1_dout),
.l0a_dout(l0a_dout),
.l0b_dout(l0b_dout)
);
endmodule
module l3_reader #(
parameter int W = 8
) (
l3_if l3,
output logic [W-1:0] dout
);
assign dout = l3.dut_out;
endmodule
module l3_driver #(
parameter int W = 8
) (
input logic clk,
l3_if l3
);
always_ff @(posedge clk) l3.dut_out <= l3.tb_in ^ W'('1);
endmodule
module l3_handler #(
parameter int W = 8,
parameter int L0A_W = 8
) (
input logic clk,
l3_if l3,
output logic [W-1:0] l3_dout,
output logic [W*2-1:0] l2a_dout,
output logic [W*4-1:0] l1_2a_dout,
output logic [L0A_W-1:0] l0a_2a_dout,
output logic [7:0] l0b_2a_dout,
output logic [7:0] l2b_dout,
output logic [15:0] l1_2b_dout,
output logic [L0A_W-1:0] l0a_2b_dout,
output logic [7:0] l0b_2b_dout
);
// Use reader/driver submodules instead of direct access
l3_reader #(W) m_rdr (
.l3(l3),
.dout(l3_dout)
);
l3_driver #(W) m_drv (
.clk(clk),
.l3(l3)
);
// Still instantiate l2_handlers for nested ports
l2_handler #(W * 2, L0A_W) m_l2a (
.clk(clk),
.l2(l3.l2a),
.l2_dout(l2a_dout),
.l1_dout(l1_2a_dout),
.l0a_dout(l0a_2a_dout),
.l0b_dout(l0b_2a_dout)
);
l2_handler #(8, L0A_W) m_l2b (
.clk(clk),
.l2(l3.l2b),
.l2_dout(l2b_dout),
.l1_dout(l1_2b_dout),
.l0a_dout(l0a_2b_dout),
.l0b_dout(l0b_2b_dout)
);
endmodule
module l4_handler #(
parameter int W = 8,
parameter int L0A_W = 8
) (
input logic clk,
l4_if l4,
output logic [W-1:0] l4_dout,
output logic [W*2-1:0] l3_dout,
output logic [W*4-1:0] l2a_dout,
output logic [W*8-1:0] l1_2a_dout,
output logic [L0A_W-1:0] l0a_2a_dout,
output logic [7:0] l0b_2a_dout,
output logic [7:0] l2b_dout,
output logic [15:0] l1_2b_dout,
output logic [L0A_W-1:0] l0a_2b_dout,
output logic [7:0] l0b_2b_dout
);
always_ff @(posedge clk) l4.dut_out <= l4.tb_in ^ W'('1);
assign l4_dout = l4.dut_out;
l3_handler #(W * 2, L0A_W) m_l3 (
.clk(clk),
.l3(l4.l3),
.l3_dout(l3_dout),
.l2a_dout(l2a_dout),
.l1_2a_dout(l1_2a_dout),
.l0a_2a_dout(l0a_2a_dout),
.l0b_2a_dout(l0b_2a_dout),
.l2b_dout(l2b_dout),
.l1_2b_dout(l1_2b_dout),
.l0a_2b_dout(l0a_2b_dout),
.l0b_2b_dout(l0b_2b_dout)
);
endmodule
module t;
logic clk = 0;
int cyc = 0;
localparam int TOP_W = 4;
localparam int L0A_W = 16;
l4_if #(TOP_W, L0A_W) inst ();
logic [TOP_W-1:0] l4_dout;
logic [TOP_W*2-1:0] l3_dout;
logic [TOP_W*4-1:0] l2a_dout;
logic [TOP_W*8-1:0] l1_2a_dout;
logic [L0A_W-1:0] l0a_2a_dout;
logic [7:0] l0b_2a_dout;
logic [7:0] l2b_dout;
logic [15:0] l1_2b_dout;
logic [L0A_W-1:0] l0a_2b_dout;
logic [7:0] l0b_2b_dout;
l4_handler #(TOP_W, L0A_W) m_l4 (
.clk(clk),
.l4(inst),
.l4_dout(l4_dout),
.l3_dout(l3_dout),
.l2a_dout(l2a_dout),
.l1_2a_dout(l1_2a_dout),
.l0a_2a_dout(l0a_2a_dout),
.l0b_2a_dout(l0b_2a_dout),
.l2b_dout(l2b_dout),
.l1_2b_dout(l1_2b_dout),
.l0a_2b_dout(l0a_2b_dout),
.l0b_2b_dout(l0b_2b_dout)
);
always #5 clk = ~clk;
always_ff @(posedge clk) begin
inst.tb_in <= cyc[TOP_W-1:0];
inst.l3.tb_in <= cyc[TOP_W*2-1:0] + (TOP_W * 2)'(1);
inst.l3.l2a.tb_in <= cyc[TOP_W*4-1:0] + (TOP_W * 4)'(2);
inst.l3.l2a.l1.tb_in <= cyc[TOP_W*8-1:0] + (TOP_W * 8)'(3);
inst.l3.l2a.l1.l0a.tb_in <= cyc[L0A_W-1:0] + L0A_W'(4);
inst.l3.l2a.l1.l0b.tb_in <= cyc[7:0] + 8'd5;
inst.l3.l2b.tb_in <= cyc[7:0] + 8'd6;
inst.l3.l2b.l1.tb_in <= cyc[15:0] + 16'd7;
inst.l3.l2b.l1.l0a.tb_in <= cyc[L0A_W-1:0] + L0A_W'(8);
inst.l3.l2b.l1.l0b.tb_in <= cyc[7:0] + 8'd9;
end
logic [TOP_W-1:0] exp_l4;
logic [TOP_W*2-1:0] exp_l3;
logic [TOP_W*4-1:0] exp_l2a;
logic [TOP_W*8-1:0] exp_l1_2a;
logic [L0A_W-1:0] exp_l0a_2a;
logic [7:0] exp_l0b_2a;
logic [7:0] exp_l2b;
logic [15:0] exp_l1_2b;
logic [L0A_W-1:0] exp_l0a_2b;
logic [7:0] exp_l0b_2b;
always_ff @(posedge clk) begin
exp_l4 <= inst.tb_in ^ TOP_W'('1);
exp_l3 <= inst.l3.tb_in ^ (TOP_W * 2)'('1);
exp_l2a <= inst.l3.l2a.tb_in ^ (TOP_W * 4)'('1);
exp_l1_2a <= inst.l3.l2a.l1.tb_in ^ (TOP_W * 8)'('1);
exp_l0a_2a <= inst.l3.l2a.l1.l0a.tb_in ^ L0A_W'('1);
exp_l0b_2a <= inst.l3.l2a.l1.l0b.tb_in ^ 8'hFF;
exp_l2b <= inst.l3.l2b.tb_in ^ 8'hFF;
exp_l1_2b <= inst.l3.l2b.l1.tb_in ^ 16'hFFFF;
exp_l0a_2b <= inst.l3.l2b.l1.l0a.tb_in ^ L0A_W'('1);
exp_l0b_2b <= inst.l3.l2b.l1.l0b.tb_in ^ 8'hFF;
end
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc > 3) begin
if (l4_dout !== exp_l4) begin
$display("FAIL cyc=%0d: l4_dout=%h expected %h", cyc, l4_dout, exp_l4);
$stop;
end
if (l3_dout !== exp_l3) begin
$display("FAIL cyc=%0d: l3_dout=%h expected %h", cyc, l3_dout, exp_l3);
$stop;
end
if (l2a_dout !== exp_l2a) begin
$display("FAIL cyc=%0d: l2a_dout=%h expected %h", cyc, l2a_dout, exp_l2a);
$stop;
end
if (l1_2a_dout !== exp_l1_2a) begin
$display("FAIL cyc=%0d: l1_2a_dout=%h expected %h", cyc, l1_2a_dout, exp_l1_2a);
$stop;
end
if (l0a_2a_dout !== exp_l0a_2a) begin
$display("FAIL cyc=%0d: l0a_2a_dout=%h expected %h", cyc, l0a_2a_dout, exp_l0a_2a);
$stop;
end
if (l0b_2a_dout !== exp_l0b_2a) begin
$display("FAIL cyc=%0d: l0b_2a_dout=%h expected %h", cyc, l0b_2a_dout, exp_l0b_2a);
$stop;
end
if (l2b_dout !== exp_l2b) begin
$display("FAIL cyc=%0d: l2b_dout=%h expected %h", cyc, l2b_dout, exp_l2b);
$stop;
end
if (l1_2b_dout !== exp_l1_2b) begin
$display("FAIL cyc=%0d: l1_2b_dout=%h expected %h", cyc, l1_2b_dout, exp_l1_2b);
$stop;
end
if (l0a_2b_dout !== exp_l0a_2b) begin
$display("FAIL cyc=%0d: l0a_2b_dout=%h expected %h", cyc, l0a_2b_dout, exp_l0a_2b);
$stop;
end
if (l0b_2b_dout !== exp_l0b_2b) begin
$display("FAIL cyc=%0d: l0b_2b_dout=%h expected %h", cyc, l0b_2b_dout, exp_l0b_2b);
$stop;
end
end
if (cyc == 20) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule