373 lines
9.4 KiB
Systemverilog
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
|