diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index f1efe8a20..51e4b7c7d 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -120,6 +120,7 @@ Han Qi Harald Heckmann Hennadii Chernyshchyk hjsanjana +Hongseok Choi Howard Su Huang Rui Huanghuang Zhou diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 73fdfe25f..8b7df8b58 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -5288,12 +5288,13 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->dotted(m_ds.m_dotText); // Maybe "" // Only flag FTaskRefs under generate-if/case blocks that may be // pruned. GenFor and plain begin-blocks won't be pruned by V3Param. + // A generate-case block's parent is the GenCaseItem, not the GenCase. // VarXRef uses the broader m_genBlk flag (set for all GenBlocks) // because genfor unrolling also removes variables. if (m_ds.m_genBlk && m_ds.m_dotSymp) { const AstNode* const blkp = m_ds.m_dotSymp->nodep(); if (VN_IS(blkp, GenBlock) - && (VN_IS(blkp->backp(), GenIf) || VN_IS(blkp->backp(), GenCase))) { + && (VN_IS(blkp->backp(), GenIf) || VN_IS(blkp->backp(), GenCaseItem))) { nodep->containsGenBlock(true); } } diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 837649f83..fd2cf6648 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -120,17 +120,22 @@ class LinkParseVisitor final : public VNVisitor { iterateChildren(nodep); } - bool nestedIfBegin(AstGenBlock* nodep) { // Point at begin inside the GenIf + bool nestedIfBegin(AstGenBlock* nodep) { // Point at begin inside the GenIf/GenCaseItem // IEEE says directly nested item is not a new block // The genblk name will get attached to the if true/false LOWER begin block(s) // 1: GENIF // -> 1:3: GENBLOCK [IMPLIED] // nodep passed to this function // 1:3:1: GENIF // 1:3:1:2: GENBLOCK genblk1 [IMPLIED] + // Likewise for a generate case item holding only a generate if + // 1: GENCASEITEM + // -> 1:2: GENBLOCK [IMPLIED] // nodep passed to this function + // 1:2:1: GENIF const AstNode* const backp = nodep->backp(); return (nodep->implied() // User didn't provide begin/end - && VN_IS(backp, GenIf) && VN_CAST(backp, GenIf)->elsesp() == nodep - && !nodep->nextp() // No other statements under upper genif else + && ((VN_IS(backp, GenIf) && VN_CAST(backp, GenIf)->elsesp() == nodep) + || VN_IS(backp, GenCaseItem)) + && !nodep->nextp() // No other statements under upper genif else/case item && (VN_IS(nodep->itemsp(), GenIf)) // Begin has if underneath && !nodep->itemsp()->nextp()); // Has only one item } diff --git a/test_regress/t/t_function_generate_case.py b/test_regress/t/t_function_generate_case.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_function_generate_case.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_function_generate_case.v b/test_regress/t/t_function_generate_case.v new file mode 100644 index 000000000..e8d86fb42 --- /dev/null +++ b/test_regress/t/t_function_generate_case.v @@ -0,0 +1,85 @@ +// DESCRIPTION: Verilator: Verify function calls through generate-case block references +// +// When a function is defined inside a generate-case item and called via a +// dotted reference (e.g. blk.f()), the FUNCREF must survive generate +// pruning. Previously the FUNCREF could point to a function in a pruned +// case item, causing a broken-link internal error. +// +// A case item holding only a generate if is directly nested (IEEE 1800-2023 +// 27.5), so its blocks are named 'blk' in the module scope, not under a +// 'genblk' scope. +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Hongseok Choi +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`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); +// verilog_format: on + +module sub #( + parameter int P = 1 +) ( + output int o +); + // Referenced before the blocks are declared + assign o = blk.f(P); + + generate + case (P) + 1, 2: + if (P == 1) begin : blk + int w = 1; + function automatic int f(input int i); + f = i + 10; + endfunction + end + else begin : blk + int w = 2; + function automatic int f(input int i); + f = i + 20; + endfunction + end + 4: begin : blk + int w = 4; + function automatic int f(input int i); + f = i + 40; + endfunction + end + default: + begin : blk + int w = 7; + function automatic int f(input int i); + f = i + 70; + endfunction + end + endcase + endgenerate +endmodule + +module t; + int o1; + int o2; + int o4; + int o7; + + sub #(.P(1)) u1 (.o(o1)); + sub #(.P(2)) u2 (.o(o2)); + sub #(.P(4)) u4 (.o(o4)); + sub #(.P(7)) u7 (.o(o7)); + + initial begin + #1; + `checkd(o1, 11); + `checkd(o2, 22); + `checkd(o4, 44); + `checkd(o7, 77); + `checkd(u1.blk.w, 1); + `checkd(u2.blk.w, 2); + `checkd(u4.blk.w, 4); + `checkd(u7.blk.w, 7); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule