diff --git a/src/V3Param.cpp b/src/V3Param.cpp index db786047e..7e7095a39 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -853,7 +853,8 @@ class ParamProcessor final { // Phase A: path-based fixup using ledger entries std::set ledgerFixed; { - const string cloneCP = VN_CAST(ifErrorp, Cell) ? VN_AS(ifErrorp, Cell)->name() : ""; + // Must match the cloneCellPath used by propagateClone (newname). + const string cloneCP = newModp->name(); const string srcName = srcModp->name(); UINFO(9, "iface capture FIXUP-A: srcName=" << srcName << " cloneCP='" << cloneCP << "'" << endl); @@ -991,7 +992,8 @@ class ParamProcessor final { } // Register clone entry in ledger (no AST mutation). if (AstRefDType* const clonedRefp = entry.refp->clonep()) { - const string cloneCP = cloneCellp ? cloneCellp->name() : string{}; + // Use newname (unique specialized module name) as cloneCellPath. + const string cloneCP = newname; const V3LinkDotIfaceCapture::TemplateKey tkey{ entry.ownerModp ? entry.ownerModp->name() : "", entry.refp->name(), entry.cellPath}; @@ -1791,10 +1793,13 @@ public: V3LinkDotIfaceCapture::forEach([&](const V3LinkDotIfaceCapture::CapturedEntry& entry) { if (!entry.refp || entry.cloneCellPath.empty()) return; if (entry.cellPath != cellName) return; - // Identity check: only retarget REFDTYPEs that actually live - // inside parentModp. Multiple clones share origName so name - // matching alone would let one clone overwrite another's refs. - if (V3LinkDotIfaceCapture::findOwnerModule(entry.refp) != parentModp) return; + AstNodeModule* const ownerp = V3LinkDotIfaceCapture::findOwnerModule(entry.refp); + // Only retarget REFDTYPEs owned by parentModp. + // Null owner (type-table dtypes) falls back to cloneCellPath match. + if (ownerp != parentModp + && !(ownerp == nullptr && entry.cloneCellPath == parentModp->name())) { + return; + } if (retargetRefToModule(entry, correctModp)) { UINFO(9, "retargetIfaceRefs: " << entry.refp << " -> " << correctModp->prettyNameQ() << endl); @@ -2102,6 +2107,12 @@ class ParamVisitor final : public VNVisitor { // Add to the hierarchy registry m_state.m_parentps[newModp].insert(modp); + + // Eagerly specialize nested iface cells so their types + // have correct widths before sibling module cells run. + if (VN_IS(newModp, Iface) && newModp != srcModp) { + specializeNestedIfaceCells(newModp); + } } } } @@ -2227,6 +2238,36 @@ class ParamVisitor final : public VNVisitor { }); } + // Deparameterize and constify nested interface cells within ifaceModp. + void specializeNestedIfaceCells(AstNodeModule* ifaceModp) { + for (AstNode* stmtp = ifaceModp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + AstCell* const nestedCellp = VN_CAST(stmtp, Cell); + if (!nestedCellp) continue; + if (!VN_IS(nestedCellp->modp(), Iface)) continue; + if (!nestedCellp->paramsp()) continue; + + AstNodeModule* const nestedSrcModp = nestedCellp->modp(); + if (AstNodeModule* const nestedNewModp = m_processor.nodeDeparam( + nestedCellp, nestedSrcModp, ifaceModp, ifaceModp->someInstanceName())) { + if (nestedNewModp != nestedSrcModp) { + // Constify the nested clone's params so its types have correct widths. + for (AstNode* sp = nestedNewModp->stmtsp(); sp; sp = sp->nextp()) { + if (AstVar* const varp = VN_CAST(sp, Var)) { + if (varp->isParam() && varp->valuep()) { + V3Const::constifyParamsEdit(varp); + } + } + } + // Retarget REFDTYPEs in the outer clone to the nested clone's types. + if (V3LinkDotIfaceCapture::enabled()) { + m_processor.retargetIfaceRefs(ifaceModp, nestedCellp->name()); + } + specializeNestedIfaceCells(nestedNewModp); + } + } + } + } + // Check if cell parameters reference interface ports or local interface instances bool cellParamsReferenceIfacePorts(AstCell* cellp) { if (!cellp->paramsp()) return false; @@ -2247,51 +2288,25 @@ class ParamVisitor final : public VNVisitor { return false; } - // Recursively specialize nested interface cells within a specialized interface. - // This handles parameter passthrough for nested interface hierarchies. - void specializeNestedIfaceCells(AstNodeModule* ifaceModp) { - for (AstNode* stmtp = ifaceModp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { - AstCell* const nestedCellp = VN_CAST(stmtp, Cell); - if (!nestedCellp) continue; - if (!VN_IS(nestedCellp->modp(), Iface)) continue; - if (!nestedCellp->paramsp()) continue; - if (cellParamsReferenceIfacePorts(nestedCellp)) continue; - - AstNodeModule* const nestedSrcModp = nestedCellp->modp(); - if (AstNodeModule* const nestedNewModp = m_processor.nodeDeparam( - nestedCellp, nestedSrcModp, ifaceModp, ifaceModp->someInstanceName())) { - // Recursively process nested interfaces within this nested interface - if (nestedNewModp != nestedSrcModp) specializeNestedIfaceCells(nestedNewModp); - } - } - } - // A generic visitor for cells and class refs void visitCellOrClassRef(AstNode* nodep, bool isIface) { // Must do ifaces first, so push to list and do in proper order m_strings.emplace_back(m_generateHierName); nodep->user2p(&m_strings.back()); - // For interface cells with parameters, specialize first before processing children - // Only do early specialization if parameters don't reference interface ports + // Deparameterize iface cells early so types are available for lparams. if (isIface && VN_CAST(nodep, Cell) && VN_CAST(nodep, Cell)->paramsp()) { AstCell* const cellp = VN_CAST(nodep, Cell); if (!cellParamsReferenceIfacePorts(cellp)) { AstNodeModule* const srcModp = cellp->modp(); - // DISABLED: specializeNestedIfaceCells causes early nested - // iface specialization where PARAMTYPEDTYPE child REFDTYPEs - // point to template structs instead of clone structs, - // destructively widthing the template with default (zero) - // values. See t_interface_nested_struct_param.v. - m_processor.nodeDeparam(cellp, srcModp, m_modp, m_modp->someInstanceName()); - // After the interface cell is rewired to its clone, - // retarget REFDTYPEs in the parent module that still - // reference the template interface's types. This ensures - // $bits(iface_typedef) evaluates correctly when - // widthParamsEdit runs on subsequent lparams. + AstNodeModule* const newModp + = m_processor.nodeDeparam(cellp, srcModp, m_modp, m_modp->someInstanceName()); + // Retarget template REFDTYPEs to the clone's types. if (V3LinkDotIfaceCapture::enabled() && cellp->modp() != srcModp) { m_processor.retargetIfaceRefs(m_modp, cellp->name()); } + // Specialize nested iface cells so their types are correct. + if (newModp && newModp != srcModp) { specializeNestedIfaceCells(newModp); } } } diff --git a/test_regress/t/t_iface_nested_width3.py b/test_regress/t/t_iface_nested_width3.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_iface_nested_width3.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_iface_nested_width3.v b/test_regress/t/t_iface_nested_width3.v new file mode 100644 index 000000000..2aa263459 --- /dev/null +++ b/test_regress/t/t_iface_nested_width3.v @@ -0,0 +1,275 @@ +// 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 +// +// Test: nested parameterized interface inside a types interface causes +// $bits() to evaluate with wrong struct width. +// + +package cfg3_pkg; + typedef struct packed { + int unsigned Capacity; + int unsigned Slices; + int unsigned NumThreads; + int unsigned NumIds; + } cfg_t; +endpackage + +// Inner types interface (mirrors xyz_types_if). +// Provides types derived from cfg that the outer interface imports. +interface inner_types_if #( + parameter cfg3_pkg::cfg_t cfg = '0 +) (); + typedef logic [$clog2(cfg.NumIds)-1:0] trans_id_t; + typedef logic [$clog2(cfg.NumThreads)-1:0] tl_index_t; +endinterface + +// Outer types interface (mirrors smem_types_if). +// Instantiates inner_types_if, imports types from it, and builds +// compound struct typedefs that include those imported types. +interface outer_types_if #( + parameter cfg3_pkg::cfg_t cfg = '0 +) (); + localparam int NUM_ROWS = (cfg.Capacity / cfg.Slices) / 8; + typedef logic [$clog2(NUM_ROWS)-1:0] row_addr_t; + + // Nested interface - this is the trigger. + inner_types_if #(cfg) inner_types(); + typedef inner_types.trans_id_t trans_id_t; + typedef inner_types.tl_index_t tl_index_t; + + typedef logic [$clog2(cfg.NumThreads)-1:0] pkt_nid_t; + + typedef struct packed { + pkt_nid_t src_nid; + pkt_nid_t dst_nid; + } pkt_hdr_t; + + typedef union packed { + logic [$bits(row_addr_t)+$bits(tl_index_t)+6-1:0] raw; + struct packed { + row_addr_t row_index; + tl_index_t tl_index; + logic [5:0] bit_index; + } fld; + } addr_t; + + typedef struct packed { + logic en; + trans_id_t tag; + tl_index_t tl; + logic is_read; + logic needs_resp; + } meta_t; + + typedef struct packed { + meta_t meta; + addr_t addr; + logic [63:0] data; + } rq_t; + + // Compound packet type (mirrors iq_pkt_t) + typedef struct packed { + pkt_hdr_t hdr; + rq_t payload; + } pkt_t; +endinterface + +// Width-parameterized FIFO (mirrors ring_fifo). +module fifo3 #( + parameter int p_width = 1, + parameter int p_depth = 2 +) ( + input logic clk, + input logic rst_n, + input logic [p_width-1:0] push_dat_i, + input logic push_vld_i, + output logic [p_width-1:0] front_dat_o, + output logic not_empty_o +); + logic [p_width-1:0] mem [p_depth]; + logic [$clog2(p_depth)-1:0] wptr, rptr; + logic [p_depth:0] count; + assign not_empty_o = (count != 0); + assign front_dat_o = mem[rptr]; + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + wptr <= '0; + rptr <= '0; + count <= '0; + end else if (push_vld_i) begin + mem[wptr] <= push_dat_i; + wptr <= wptr + 1; + count <= count + 1; + end + end +endmodule + +// Slice module (mirrors smem_slice). +// Receives iq_pkt_t as a TYPE PARAMETER from wrapper3, then uses +// $bits(iq_pkt_t) as a value parameter to fifo3. +// This forces $bits() evaluation during widthParamsEdit of the slice3 +// cell (in wrapper3's cell loop), before outer_types_if__Az1's nested +// inner_types_if is deparameterized. +module slice3 #( + parameter cfg3_pkg::cfg_t cfg = '0, + parameter int SLICE_IDX = 0, + parameter type iq_pkt_t = logic, + parameter type oq_pkt_t = logic +) ( + input logic clk, + input logic rst_n, + input logic in_vld, + output logic out_vld, + output iq_pkt_t fe_ot_pkt_o +); + // Local outer_types_if for other local types + outer_types_if #(cfg) types(); + typedef types.pkt_nid_t pkt_nid_t; + + iq_pkt_t rqq_in, rqq_ot; + logic rqq_ot_vld; + + assign rqq_in.hdr.src_nid = pkt_nid_t'(SLICE_IDX); + assign rqq_in.hdr.dst_nid = '0; + assign rqq_in.payload.meta.en = in_vld; + assign rqq_in.payload.meta.tag = '0; + assign rqq_in.payload.meta.tl = '0; + assign rqq_in.payload.meta.is_read = 1'b1; + assign rqq_in.payload.meta.needs_resp = 1'b0; + assign rqq_in.payload.addr = '0; + assign rqq_in.payload.data = 64'hCAFE_BABE; + + // $bits(iq_pkt_t) as value parameter - the bug trigger. + // iq_pkt_t is a type parameter whose PARAMTYPEDTYPE child REFDTYPE + // points into the outer_types_if clone's STRUCTDTYPE. When + // widthParamsEdit evaluates this during wrapper3's cell loop, + // the nested inner_types_if inside outer_types_if__Az1 hasn't + // been deparameterized, so trans_id_t/tl_index_t have wrong widths. + fifo3 #( + .p_width($bits(iq_pkt_t)), + .p_depth(4) + ) rqq ( + .clk(clk), + .rst_n(rst_n), + .push_dat_i(rqq_in), + .push_vld_i(in_vld), + .front_dat_o(rqq_ot), + .not_empty_o(rqq_ot_vld) + ); + + assign fe_ot_pkt_o = rqq_ot; + assign out_vld = rqq_ot_vld; +endmodule + +// Wrapper module (mirrors smem_top). +// Creates outer_types_if, imports pkt_t, and passes it as a type +// parameter to slice3 instances in a generate loop. +module wrapper3 #( + parameter cfg3_pkg::cfg_t cfg = '0 +) ( + input logic clk, + input logic rst_n +); + outer_types_if #(cfg) types(); + typedef types.pkt_t iq_pkt_t; + typedef types.rq_t oq_pkt_t; + + // Capture $bits of the type parameter for external checking. + // If the nested inner_types_if hasn't been deparameterized before + // this is evaluated, the width will be wrong (e.g. 90 instead of 92). + localparam int PKT_WIDTH = $bits(iq_pkt_t); + + logic [cfg.NumThreads-1:0] out_vld; + + generate + for (genvar i = 0; i < cfg.NumThreads; i++) begin : gen_slices + iq_pkt_t fe_pkt; + slice3 #( + .cfg(cfg), + .SLICE_IDX(i), + .iq_pkt_t(iq_pkt_t), + .oq_pkt_t(oq_pkt_t) + ) u_slice ( + .clk(clk), + .rst_n(rst_n), + .in_vld(1'b1), + .out_vld(out_vld[i]), + .fe_ot_pkt_o(fe_pkt) + ); + end + endgenerate +endmodule + +module t; + logic clk = 0; + always #5 clk = ~clk; + logic rst_n = 0; + + int cyc = 0; + + localparam cfg3_pkg::cfg_t MY_CFG = '{ + Capacity: 8192, + Slices: 8, + NumThreads: 4, + NumIds: 16 + }; + // Expected widths: + // trans_id_t = $clog2(16) = 4 bits + // tl_index_t = $clog2(4) = 2 bits + // row_addr_t = $clog2((8192/8)/8) = $clog2(128) = 7 bits + // pkt_nid_t = $clog2(4) = 2 bits + // pkt_hdr_t = 2+2 = 4 bits + // addr_t = 7+2+6 = 15 bits + // meta_t = 1+4+2+1+1 = 9 bits + // rq_t = 9+15+64 = 88 bits + // pkt_t = 4+88 = 92 bits + + // Compute expected pkt_t width from first principles. + // These computations happen in the testbench module context where + // the cfg parameter values are known constants, not through the + // nested interface PARAMTYPEDTYPE chain. + localparam int EXP_TRANS_ID_W = $clog2(MY_CFG.NumIds); // 4 + localparam int EXP_TL_INDEX_W = $clog2(MY_CFG.NumThreads); // 2 + localparam int EXP_ROW_ADDR_W = $clog2((MY_CFG.Capacity / MY_CFG.Slices) / 8); // 7 + localparam int EXP_PKT_NID_W = $clog2(MY_CFG.NumThreads); // 2 + localparam int EXP_PKT_HDR_W = 2 * EXP_PKT_NID_W; // 4 + localparam int EXP_ADDR_W = EXP_ROW_ADDR_W + EXP_TL_INDEX_W + 6; // 15 + localparam int EXP_META_W = 1 + EXP_TRANS_ID_W + EXP_TL_INDEX_W + 1 + 1; // 9 + localparam int EXP_RQ_W = EXP_META_W + EXP_ADDR_W + 64; // 88 + localparam int EXP_PKT_W = EXP_PKT_HDR_W + EXP_RQ_W; // 92 + + wrapper3 #(.cfg(MY_CFG)) u_wrapper ( + .clk(clk), + .rst_n(rst_n) + ); + + // Self-check: verify that $bits(pkt_t) as seen through the nested + // interface type parameter chain matches the expected width. + // If the bug is present, u_wrapper.PKT_WIDTH will be < EXP_PKT_W + // because trans_id_t was evaluated with the template default + // cfg.NumIds=0 instead of the actual value 16. + initial begin + if (u_wrapper.PKT_WIDTH != EXP_PKT_W) begin + $display("%%Error: t_iface_nested_width3.v: $bits(pkt_t) = %0d, expected %0d", u_wrapper.PKT_WIDTH, EXP_PKT_W); + $stop; + end + end + + always @(posedge clk) begin + cyc <= cyc + 1; + if (cyc == 2) rst_n <= 1; + if (cyc > 5) begin + if (u_wrapper.out_vld !== {MY_CFG.NumThreads{1'b1}}) begin + $display("FAIL cyc=%0d: out_vld=%b", cyc, u_wrapper.out_vld); + $stop; + end + end + if (cyc == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_iface_param_type_derived_range.py b/test_regress/t/t_iface_param_type_derived_range.py new file mode 100755 index 000000000..46d1fe4c0 --- /dev/null +++ b/test_regress/t/t_iface_param_type_derived_range.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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('simulator') + +test.compile(verilator_flags2=['--binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_iface_param_type_derived_range.v b/test_regress/t/t_iface_param_type_derived_range.v new file mode 100644 index 000000000..f3981b111 --- /dev/null +++ b/test_regress/t/t_iface_param_type_derived_range.v @@ -0,0 +1,203 @@ +// 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 +// +// Test: struct typedef from parameterized interface with derived +// localparam used as type parameter to a sub-module cell. +// + +package cfg_pkg; + typedef struct packed { + int unsigned Capacity; + int unsigned Slices; + int unsigned NumThreads; + } cfg_t; +endpackage + +// Types interface: derives localparam from cfg, uses in typedef range, +// builds union/struct containing that typedef. +// On the template default (cfg='0), Capacity/Slices = 0/0 = X. +interface types_if #( + parameter cfg_pkg::cfg_t cfg = '0 +) (); + localparam int NUM_ROWS = (cfg.Capacity / cfg.Slices) / 8; + typedef logic [$clog2(NUM_ROWS)-1:0] row_addr_t; + typedef logic [$clog2(cfg.NumThreads)-1:0] tl_addr_t; + + typedef union packed { + logic [$bits(row_addr_t)+$bits(tl_addr_t)+6-1:0] raw; + struct packed { + row_addr_t row_index; + tl_addr_t tl_index; + logic [5:0] bit_index; + } fld; + } addr_t; + + typedef struct packed { + logic en; + logic [3:0] tag; + logic is_read; + } meta_t; + + // Compound packet type (mirrors iq_pkt_t) + typedef struct packed { + meta_t meta; + addr_t addr; + logic [63:0] data; + } pkt_t; +endinterface + +// Generic type-parameterized register (mirrors tflop_nr) +module tflop_nr #(parameter type T = logic) ( + input logic clk, + input logic rst_n, + output T q_o, + input T d_i +); + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) q_o <= '0; + else q_o <= d_i; + end +endmodule + +// Slice module: receives pkt_t as type param from wrapper, also +// instantiates types_if locally, builds local struct from local types, +// passes that struct as type param to tflop_nr. +// (mirrors smem_slice) +module slice #( + parameter cfg_pkg::cfg_t cfg = '0, + parameter int SLICE_IDX = 0, + parameter type pkt_t = logic +) ( + input logic clk, + input logic rst_n, + input logic in_vld, + input pkt_t in_pkt, + output logic out_vld, + output pkt_t out_pkt +); + // Local types_if instantiation - same cfg as wrapper's + types_if #(cfg) types(); + + typedef types.addr_t addr_t; + typedef types.meta_t meta_t; + + // Local struct containing locally-imported interface types + typedef struct packed { + meta_t meta; + addr_t addr; + } rq_t; + + rq_t rq_d, rq_q; + + always_comb begin + rq_d = rq_q; + if (in_vld) begin + rq_d.meta = in_pkt.meta; + rq_d.addr = in_pkt.addr; + end + end + + // Pass local struct as type param to tflop_nr - this is the trigger + tflop_nr #(.T(rq_t)) rq_reg ( + .clk(clk), + .rst_n(rst_n), + .q_o(rq_q), + .d_i(rq_d) + ); + + assign out_vld = rq_q.meta.en; + assign out_pkt.meta = rq_q.meta; + assign out_pkt.addr = rq_q.addr; + assign out_pkt.data = in_pkt.data; +endmodule + +// Wrapper module: instantiates types_if, imports pkt_t, passes it as +// type param to slice instances in a generate loop. +// (mirrors smem_top) +module wrapper #( + parameter cfg_pkg::cfg_t cfg = '0 +) ( + input logic clk, + input logic rst_n +); + types_if #(cfg) types(); + + typedef types.pkt_t pkt_t; + typedef types.meta_t meta_t; + + pkt_t in_pkt; + logic in_vld; + + assign in_vld = 1'b1; + assign in_pkt.meta.en = 1'b1; + assign in_pkt.meta.tag = 4'd5; + assign in_pkt.meta.is_read = 1'b0; + assign in_pkt.addr = '0; + assign in_pkt.data = 64'hDEAD_BEEF; + + // Generate loop with slices (mirrors gen_slices in smem_top) + logic [cfg.NumThreads-1:0] out_vld; + pkt_t [cfg.NumThreads-1:0] out_pkt; + + generate + for (genvar i = 0; i < 2; i++) begin : gen_slices + slice #( + .cfg(cfg), + .SLICE_IDX(i), + .pkt_t(pkt_t) + ) u_slice ( + .clk(clk), + .rst_n(rst_n), + .in_vld(in_vld), + .in_pkt(in_pkt), + .out_vld(out_vld[i]), + .out_pkt(out_pkt[i]) + ); + end + endgenerate +endmodule + +module t; + logic clk = 0; + always #5 clk = ~clk; + logic rst_n = 0; + + int cyc = 0; + + localparam cfg_pkg::cfg_t MY_CFG = '{ + Capacity: 8192, + Slices: 8, + NumThreads: 32 + }; + // Expected: NUM_ROWS = (8192/8)/8 = 128 + // row_addr_t = logic [6:0] (7 bits) + // tl_addr_t = logic [4:0] (5 bits) + // addr_t.raw = logic [17:0] (7+5+6 = 18 bits) + + wrapper #(.cfg(MY_CFG)) u_wrapper ( + .clk(clk), + .rst_n(rst_n) + ); + + always @(posedge clk) begin + cyc <= cyc + 1; + if (cyc == 2) rst_n <= 1; + if (cyc > 5) begin + if (u_wrapper.out_vld[0] !== 1'b1) begin + $display("FAIL cyc=%0d: out_vld[0]=%b expected 1", cyc, u_wrapper.out_vld[0]); + $stop; + end + if (u_wrapper.out_pkt[0].meta.tag !== 4'd5) begin + $display("FAIL cyc=%0d: tag=%0d expected 5", cyc, u_wrapper.out_pkt[0].meta.tag); + $stop; + end + end + if (cyc == 20) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_param_type_from_iface_struct.py b/test_regress/t/t_param_type_from_iface_struct.py index eee20b937..44d191244 100755 --- a/test_regress/t/t_param_type_from_iface_struct.py +++ b/test_regress/t/t_param_type_from_iface_struct.py @@ -7,17 +7,11 @@ # SPDX-FileCopyrightText: 2026 Wilson Snyder # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 -# Verifies that skipWidthForTemplateStruct fires in V3Param::cellPinCleanup -# when struct typedefs from a nested parameterized interface are passed as -# type parameters through two levels of interface nesting. - import vltest_bootstrap test.scenarios('vlt') -test.compile(v_flags2=["--binary --stats"]) - -test.file_grep(test.stats, r'Param, Template struct width skips\s+(\d+)', 2) +test.compile(v_flags2=["--binary"]) test.execute() diff --git a/test_regress/t/t_paramgraph_comined_iface_stats.py b/test_regress/t/t_paramgraph_comined_iface_stats.py index 53dbc323b..689830310 100755 --- a/test_regress/t/t_paramgraph_comined_iface_stats.py +++ b/test_regress/t/t_paramgraph_comined_iface_stats.py @@ -21,9 +21,9 @@ test.compile(v_flags2=["--binary --stats"]) test.file_grep(test.stats, r'IfaceCapture, Entries total\s+(\d+)', 18) test.file_grep(test.stats, r'IfaceCapture, Entries template\s+(\d+)', 8) test.file_grep(test.stats, r'IfaceCapture, Entries cloned\s+(\d+)', 10) -test.file_grep(test.stats, r'IfaceCapture, Ledger fixups in V3Param\s+(\d+)', 10) +test.file_grep(test.stats, r'IfaceCapture, Ledger fixups in V3Param\s+(\d+)', 8) test.file_grep(test.stats, r'IfaceCapture, Wrong-clone refs fixed\s+(\d+)', 10) -test.file_grep(test.stats, r'IfaceCapture, Dead refs fixed in modules\s+(\d+)', 6) +test.file_grep(test.stats, r'IfaceCapture, Dead refs fixed in modules\s+(\d+)', 2) test.execute() diff --git a/test_regress/t/t_paramgraph_iface_template_nested_stats.py b/test_regress/t/t_paramgraph_iface_template_nested_stats.py index c6627c095..5ba344e1f 100755 --- a/test_regress/t/t_paramgraph_iface_template_nested_stats.py +++ b/test_regress/t/t_paramgraph_iface_template_nested_stats.py @@ -18,12 +18,12 @@ test.top_filename = "t/t_paramgraph_iface_template_nested.v" test.compile(v_flags2=["--binary --stats"]) -test.file_grep(test.stats, r'IfaceCapture, Entries total\s+(\d+)', 21) +test.file_grep(test.stats, r'IfaceCapture, Entries total\s+(\d+)', 25) test.file_grep(test.stats, r'IfaceCapture, Entries template\s+(\d+)', 11) -test.file_grep(test.stats, r'IfaceCapture, Entries cloned\s+(\d+)', 10) -test.file_grep(test.stats, r'IfaceCapture, Ledger fixups in V3Param\s+(\d+)', 7) -test.file_grep(test.stats, r'IfaceCapture, Wrong-clone refs fixed\s+(\d+)', 8) -test.file_grep(test.stats, r'IfaceCapture, Dead refs fixed in modules\s+(\d+)', 4) +test.file_grep(test.stats, r'IfaceCapture, Entries cloned\s+(\d+)', 14) +test.file_grep(test.stats, r'IfaceCapture, Ledger fixups in V3Param\s+(\d+)', 5) +test.file_grep(test.stats, r'IfaceCapture, Wrong-clone refs fixed\s+(\d+)', 10) +test.file_grep(test.stats, r'IfaceCapture, Dead refs fixed in modules\s+(\d+)', 2) test.execute() diff --git a/test_regress/t/t_paramgraph_nested_iface_typedef_stats.py b/test_regress/t/t_paramgraph_nested_iface_typedef_stats.py index 4bcad9664..841c59c88 100755 --- a/test_regress/t/t_paramgraph_nested_iface_typedef_stats.py +++ b/test_regress/t/t_paramgraph_nested_iface_typedef_stats.py @@ -18,12 +18,12 @@ test.top_filename = "t/t_paramgraph_nested_iface_typedef.v" test.compile(v_flags2=["--binary --stats"]) -test.file_grep(test.stats, r'IfaceCapture, Entries total\s+(\d+)', 18) +test.file_grep(test.stats, r'IfaceCapture, Entries total\s+(\d+)', 20) test.file_grep(test.stats, r'IfaceCapture, Entries template\s+(\d+)', 8) -test.file_grep(test.stats, r'IfaceCapture, Entries cloned\s+(\d+)', 10) -test.file_grep(test.stats, r'IfaceCapture, Ledger fixups in V3Param\s+(\d+)', 12) -test.file_grep(test.stats, r'IfaceCapture, Wrong-clone refs fixed\s+(\d+)', 12) -test.file_grep(test.stats, r'IfaceCapture, Dead refs fixed in modules\s+(\d+)', 10) +test.file_grep(test.stats, r'IfaceCapture, Entries cloned\s+(\d+)', 12) +test.file_grep(test.stats, r'IfaceCapture, Ledger fixups in V3Param\s+(\d+)', 8) +test.file_grep(test.stats, r'IfaceCapture, Wrong-clone refs fixed\s+(\d+)', 14) +test.file_grep(test.stats, r'IfaceCapture, Dead refs fixed in modules\s+(\d+)', 4) test.execute()