diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 9cdc71c78..fa471cc48 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2649,13 +2649,167 @@ class LinkDotIfaceVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep), nodep); } } + + // Helper to extract a dotted path string from an AstDot tree + // Returns empty string and sets hasPartSelect=true if part-select detected + string extractDottedPath(AstNode* nodep, bool& hasPartSelect) { + if (AstParseRef* const refp = VN_CAST(nodep, ParseRef)) { + return refp->name(); + } else if (AstVarRef* const refp = VN_CAST(nodep, VarRef)) { + return refp->name(); + } else if (AstDot* const dotp = VN_CAST(nodep, Dot)) { + const string lhs = extractDottedPath(dotp->lhsp(), hasPartSelect); + const string rhs = extractDottedPath(dotp->rhsp(), hasPartSelect); + if (lhs.empty()) return rhs; + if (rhs.empty()) return lhs; + return lhs + "." + rhs; + } else if (VN_IS(nodep, SelBit) || VN_IS(nodep, SelExtract)) { + hasPartSelect = true; + return ""; + } + return ""; + } + + // Helper to resolve remaining path through a nested interface + // When findDotted() partially matches (okSymp set, baddot non-empty), + // this follows the interface type to resolve the remaining path. + // Returns the resolved symbol, or nullptr if not found. + // On success, clears baddot; on partial match of multi-level path, updates baddot. + VSymEnt* resolveNestedInterfacePath(FileLine* fl, VSymEnt* okSymp, string& baddot) { + if (!okSymp || baddot.empty()) return nullptr; + + static constexpr int MAX_NESTING_DEPTH = 64; + VSymEnt* curOkSymp = okSymp; + + for (int depth = 0; depth < MAX_NESTING_DEPTH; ++depth) { + // Try to get interface from the partially-matched symbol + AstIface* ifacep = nullptr; + if (const AstCell* const cellp = VN_CAST(curOkSymp->nodep(), Cell)) { + ifacep = VN_CAST(cellp->modp(), Iface); + } else if (const AstVar* const varp = VN_CAST(curOkSymp->nodep(), Var)) { + if (varp->isIfaceRef()) { + if (const AstIfaceRefDType* const ifaceRefp + = LinkDotState::ifaceRefFromArray(varp->dtypep())) { + ifacep = ifaceRefp->ifaceViaCellp(); + } + } + } + + if (!ifacep || !m_statep->existsNodeSym(ifacep)) return nullptr; + + VSymEnt* const ifaceSymp = m_statep->getNodeSym(ifacep); + + if (baddot.find('.') == string::npos) { + // Simple identifier - direct lookup + VSymEnt* const symp = ifaceSymp->findIdFallback(baddot); + if (symp) baddot.clear(); + return symp; + } + + // Multi-level path - use findDotted for partial resolution + string remainingBaddot; + VSymEnt* remainingOkSymp = nullptr; + VSymEnt* const symp = m_statep->findDotted(fl, ifaceSymp, baddot, remainingBaddot, + remainingOkSymp, true); + if (symp) { + baddot = remainingBaddot; + return symp; + } + + // findDotted partially matched - check progress + if (remainingBaddot == baddot || !remainingOkSymp) return nullptr; + + // Continue resolving with updated state + baddot = remainingBaddot; + curOkSymp = remainingOkSymp; + } + + UINFO(1, "Nested interface resolution depth limit exceeded at " << fl << endl); + return nullptr; + } + + // Resolve a modport expression to find the referenced symbol + VSymEnt* resolveModportExpression(FileLine* fl, AstNodeExpr* exprp) { + UINFO(5, " resolveModportExpression: " << exprp << endl); + VSymEnt* symp = nullptr; + if (AstParseRef* const refp = VN_CAST(exprp, ParseRef)) { + // Simple variable reference: modport mp(input .a(sig_a)) + symp = m_curSymp->findIdFallback(refp->name()); + } else if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) { + // Already resolved VarRef (can happen if iterateChildren resolved it) + if (refp->varScopep()) { + // During scope creation, VarRef may have VarScope already linked + symp = m_statep->getNodeSym(refp->varScopep()); + } else if (refp->varp()) { + symp = m_curSymp->findIdFallback(refp->varp()->name()); + } + } else if (AstVarXRef* const refp = VN_CAST(exprp, VarXRef)) { + // Resolved VarXRef (dotted reference resolved by earlier pass) + if (refp->varp()) { + // For nested interfaces, the var is in a different scope + // First try to find the symbol via the var itself + if (m_statep->existsNodeSym(refp->varp())) { + symp = m_statep->getNodeSym(refp->varp()); + } else { + // Fallback: look up in current scope + symp = m_curSymp->findIdFallback(refp->varp()->name()); + } + } else if (!refp->dotted().empty()) { + // varp not set yet - use dotted path to find the symbol + // The dotted part (e.g., "base") is the interface cell, and name is the member + const string fullPath = refp->dotted() + "." + refp->name(); + string baddot; + VSymEnt* okSymp = nullptr; + symp = m_statep->findDotted(fl, m_curSymp, fullPath, baddot, okSymp, true); + // Handle nested interface path when findDotted had partial match + if (okSymp && !baddot.empty()) { + VSymEnt* const resolved = resolveNestedInterfacePath(fl, okSymp, baddot); + if (resolved) symp = resolved; + } + } + } else if (AstDot* const dotp = VN_CAST(exprp, Dot)) { + // Dotted path: modport mp(input .a(inner.sig)) + bool hasPartSelect = false; + const string dottedPath = extractDottedPath(dotp, hasPartSelect); + if (hasPartSelect) { + fl->v3warn(E_UNSUPPORTED, + "Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4)"); + } else { + string baddot; + VSymEnt* okSymp = nullptr; + symp = m_statep->findDotted(fl, m_curSymp, dottedPath, baddot, okSymp, true); + + // Handle nested interface path when findDotted had partial match + if (okSymp && !baddot.empty()) { + VSymEnt* const resolved = resolveNestedInterfacePath(fl, okSymp, baddot); + if (resolved) symp = resolved; + } + + if (!symp || !baddot.empty()) { + fl->v3error("Can't find modport expression target: " + << AstNode::prettyNameQ(dottedPath)); + symp = nullptr; + } + } + } else if (VN_IS(exprp, SelBit) || VN_IS(exprp, SelExtract)) { + // Part select expressions not yet supported + fl->v3warn(E_UNSUPPORTED, + "Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4)"); + } else { + // Other expression types not supported + fl->v3warn(E_UNSUPPORTED, + "Unsupported: Complex modport expression (IEEE 1800-2023 25.5.4)"); + } + return symp; + } + void visit(AstModportVarRef* nodep) override { // IfaceVisitor:: UINFO(5, " fiv: " << nodep); iterateChildren(nodep); VSymEnt* symp = nullptr; if (nodep->exprp()) { - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)"); + // Modport expression syntax: modport mp(input .port_name(expression)) + symp = resolveModportExpression(nodep->fileline(), nodep->exprp()); } else { symp = m_curSymp->findIdFallback(nodep->name()); } @@ -2665,11 +2819,30 @@ class LinkDotIfaceVisitor final : public VNVisitor { // Make symbol under modport that points at the _interface_'s var via the modport. // (Need modport still to test input/output markings) nodep->varp(varp); - m_statep->insertSym(m_curSymp, nodep->name(), nodep, nullptr /*package*/); + if (nodep->exprp()) { + // For modport expressions, insert symbol pointing to the underlying var (not the + // ModportVarRef which will be deleted during scope creation). The virtual port + // name maps to the real signal's var. + VSymEnt* const subSymp + = m_statep->insertSym(m_curSymp, nodep->name(), varp, nullptr /*package*/); + m_statep->insertScopeAlias(LinkDotState::SAMN_MODPORT, subSymp, symp); + } else { + // For regular modport items, insert symbol pointing to ModportVarRef for + // input/output marking tests. + m_statep->insertSym(m_curSymp, nodep->name(), nodep, nullptr /*package*/); + } } else if (AstVarScope* const vscp = VN_CAST(symp->nodep(), VarScope)) { // Make symbol under modport that points at the _interface_'s var, not the modport. nodep->varp(vscp->varp()); - m_statep->insertSym(m_curSymp, nodep->name(), vscp, nullptr /*package*/); + // Only insert symbol for modport expression virtual ports (exprp set). + // For regular modport items, don't insert into the shared modport table because + // each instance would overwrite the previous one, causing wrong VarScope lookups. + // Regular items are found via the modport's fallback to the interface. + if (nodep->exprp()) { + VSymEnt* const subSymp + = m_statep->insertSym(m_curSymp, nodep->name(), vscp, nullptr /*package*/); + m_statep->insertScopeAlias(LinkDotState::SAMN_MODPORT, subSymp, symp); + } } else { nodep->v3error("Modport item is not a variable: " << nodep->prettyNameQ()); } @@ -2866,6 +3039,46 @@ class LinkDotResolveVisitor final : public VNVisitor { return nullptr; } } + + // Look up a virtual port through modport expression mapping. + // When a dotted reference uses a modport with expression syntax, the virtual port name + // maps to the real signal. This helper resolves that mapping during scope creation. + // Returns the VarScope for the real signal, or nullptr if not found. + AstVarScope* findVirtualPortVarScope(VSymEnt* dotSymp, const string& portName) { + const AstVarScope* const dotVscp = VN_CAST(dotSymp->nodep(), VarScope); + const AstVar* const dotVarp = dotVscp ? dotVscp->varp() : nullptr; + if (!dotVarp) return nullptr; + + AstNodeDType* dtypep = dotVarp->childDTypep(); + if (!dtypep) dtypep = dotVarp->subDTypep(); + if (!dtypep) return nullptr; + const AstIfaceRefDType* const ifaceRefp = VN_CAST(dtypep, IfaceRefDType); + if (!ifaceRefp || !ifaceRefp->modportp() || !ifaceRefp->ifaceViaCellp()) return nullptr; + + // Get the interface cell's symbol table + VSymEnt* const ifaceCellSymp = m_statep->getNodeSym(ifaceRefp->ifaceViaCellp()); + + // Look up modport by name in interface cell + VSymEnt* const modportSymp = ifaceCellSymp->findIdFallback(ifaceRefp->modportp()->name()); + if (!modportSymp) return nullptr; + + // Look up virtual port name in modport symbol table + string baddot; + VSymEnt* const virtPortp = m_statep->findSymPrefixed(modportSymp, portName, baddot, true); + if (!virtPortp) return nullptr; + + // Symbol may point to VarScope or Var + if (AstVarScope* vscp = VN_CAST(virtPortp->nodep(), VarScope)) return vscp; + + // Symbol points to Var - look up VarScope by name in interface cell + if (const AstVar* const realVarp = VN_CAST(virtPortp->nodep(), Var)) { + VSymEnt* const realFoundp + = m_statep->findSymPrefixed(ifaceCellSymp, realVarp->name(), baddot, true); + return realFoundp ? VN_CAST(realFoundp->nodep(), VarScope) : nullptr; + } + return nullptr; + } + AstNodeStmt* addImplicitSuperNewCall(AstFunc* const nodep, const AstClassExtends* const classExtendsp) { // Returns the added node @@ -3863,6 +4076,24 @@ class LinkDotResolveVisitor final : public VNVisitor { } else { foundp = m_ds.m_dotSymp->findIdFlat(nodep->name()); } + // If not found in modport, check interface fallback for parameters. + // Parameters are always visible through a modport (IEEE 1800-2023 25.5). + // This mirrors the VarXRef modport parameter fallback in visit(AstVarXRef). + if (!foundp && VN_IS(m_ds.m_dotSymp->nodep(), Modport) + && m_ds.m_dotSymp->fallbackp()) { + VSymEnt* const ifaceFoundp + = m_ds.m_dotSymp->fallbackp()->findIdFlat(nodep->name()); + if (ifaceFoundp) { + if (const AstVar* const varp = VN_CAST(ifaceFoundp->nodep(), Var)) { + if (varp->isParam()) foundp = ifaceFoundp; + } + } + } + // When flat lookup in modport fails, provide dotSymp for error diagnostics + // so the "Known scopes under..." hint appears (restores pre-routing behavior) + if (!foundp && !okSymp && VN_IS(m_ds.m_dotSymp->nodep(), Modport)) { + okSymp = m_ds.m_dotSymp; + } if (foundp) { UINFO(9, indent() << "found=se" << cvtToHex(foundp) << " exp=" << expectWhat << " n=" << foundp->nodep()); @@ -3959,7 +4190,12 @@ class LinkDotResolveVisitor final : public VNVisitor { // Really this is a scope reference into an interface UINFO(9, indent() << "varref-ifaceref " << m_ds.m_dotText << " " << nodep); m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name()); - m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->ifaceViaCellp()); + // If modport specified, use modport symbol table for lookup of virtual ports + if (ifacerefp->modportp() && m_statep->existsNodeSym(ifacerefp->modportp())) { + m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->modportp()); + } else { + m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->ifaceViaCellp()); + } m_ds.m_dotPos = DP_SCOPE; ok = true; AstNode* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::READ}; @@ -4109,6 +4345,12 @@ class LinkDotResolveVisitor final : public VNVisitor { m_ds.m_dotSymp = foundp; if (m_ds.m_dotText != "") m_ds.m_dotText += "." + nodep->name(); ok = m_ds.m_dotPos == DP_SCOPE || m_ds.m_dotPos == DP_FIRST; + } else if (const AstModportClockingRef* const clockingRefp + = VN_CAST(foundp->nodep(), ModportClockingRef)) { + // Clocking block accessed through a modport - redirect to actual clocking + m_ds.m_dotSymp = m_statep->getNodeSym(clockingRefp->clockingp()); + if (m_ds.m_dotText != "") m_ds.m_dotText += "." + nodep->name(); + ok = m_ds.m_dotPos == DP_SCOPE || m_ds.m_dotPos == DP_FIRST; } else if (const AstNodeFTask* const ftaskp = VN_CAST(foundp->nodep(), NodeFTask)) { if (!ftaskp->isFunction() || ftaskp->classMethod()) { ok = m_ds.m_dotPos == DP_NONE; @@ -4396,7 +4638,7 @@ class LinkDotResolveVisitor final : public VNVisitor { } bool modport = false; - if (const AstVar* varp = VN_CAST(dotSymp->nodep(), Var)) { + if (const AstVar* const varp = VN_CAST(dotSymp->nodep(), Var)) { if (const AstIfaceRefDType* const ifaceRefp = VN_CAST(varp->childDTypep(), IfaceRefDType)) { if (ifaceRefp->modportp()) { @@ -4474,7 +4716,9 @@ class LinkDotResolveVisitor final : public VNVisitor { } else { VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, true); - AstVarScope* vscp = foundp ? VN_AS(foundp->nodep(), VarScope) : nullptr; + AstVarScope* vscp = foundp ? VN_CAST(foundp->nodep(), VarScope) : nullptr; + // Handle modport expression virtual ports + if (!vscp) vscp = findVirtualPortVarScope(dotSymp, nodep->name()); // If found, check if it's ok to access in case it's in a hier_block if (vscp && errorHierNonPort(nodep, vscp->varp(), dotSymp)) return; if (!vscp) { diff --git a/test_regress/t/t_interface_generic_modport_bad3.out b/test_regress/t/t_interface_generic_modport_bad3.out index a822cf676..3a09a3cf8 100644 --- a/test_regress/t/t_interface_generic_modport_bad3.out +++ b/test_regress/t/t_interface_generic_modport_bad3.out @@ -1,7 +1,7 @@ -%Error: t/t_interface_generic_modport_bad3.v:18:11: Can't find definition of 'v' in dotted signal: 'a.v' +%Error: t/t_interface_generic_modport_bad3.v:18:11: Can't find definition of 'v' in dotted variable/method: 'a.v' : ... note: In instance 't.genericModule' 18 | if (a.v != 7) $stop; | ^ - ... Known scopes under 'v': + ... Known scopes under 'a': ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to diff --git a/test_regress/t/t_interface_mismodport_bad.out b/test_regress/t/t_interface_mismodport_bad.out index a8b307c1f..598cdd3df 100644 --- a/test_regress/t/t_interface_mismodport_bad.out +++ b/test_regress/t/t_interface_mismodport_bad.out @@ -1,6 +1,6 @@ -%Error: t/t_interface_mismodport_bad.v:32:12: Can't find definition of 'bad' in dotted signal: 'isub.bad' +%Error: t/t_interface_mismodport_bad.v:32:12: Can't find definition of 'bad' in dotted variable/method: 'isub.bad' 32 | isub.bad = i_value; | ^~~ - ... Known scopes under 'bad': + ... Known scopes under 'isub': ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: Exiting due to diff --git a/test_regress/t/t_interface_modport_expr.out b/test_regress/t/t_interface_modport_expr.out deleted file mode 100644 index 277926b00..000000000 --- a/test_regress/t/t_interface_modport_expr.out +++ /dev/null @@ -1,39 +0,0 @@ -%Error-UNSUPPORTED: t/t_interface_modport_expr.v:15:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) - 15 | modport mp1(input .a(sig_a), output .b(sig_b)); - | ^ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_interface_modport_expr.v:15:22: Modport item not found: 'a' - 15 | modport mp1(input .a(sig_a), output .b(sig_b)); - | ^ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error-UNSUPPORTED: t/t_interface_modport_expr.v:15:40: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) - 15 | modport mp1(input .a(sig_a), output .b(sig_b)); - | ^ -%Error: t/t_interface_modport_expr.v:15:40: Modport item not found: 'b' - 15 | modport mp1(input .a(sig_a), output .b(sig_b)); - | ^ -%Error-UNSUPPORTED: t/t_interface_modport_expr.v:16:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) - 16 | modport mp2(input .a(sig_c), output .b(sig_d)); - | ^ -%Error: t/t_interface_modport_expr.v:16:22: Modport item not found: 'a' - 16 | modport mp2(input .a(sig_c), output .b(sig_d)); - | ^ -%Error-UNSUPPORTED: t/t_interface_modport_expr.v:16:40: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) - 16 | modport mp2(input .a(sig_c), output .b(sig_d)); - | ^ -%Error: t/t_interface_modport_expr.v:16:40: Modport item not found: 'b' - 16 | modport mp2(input .a(sig_c), output .b(sig_d)); - | ^ -%Error: t/t_interface_modport_expr.v:28:18: Can't find definition of 'a' in dotted variable/method: 'i.a' - 28 | assign i.b = i.a; - | ^ -%Error: t/t_interface_modport_expr.v:28:12: Can't find definition of 'b' in dotted variable/method: 'i.b' - 28 | assign i.b = i.a; - | ^ -%Error: t/t_interface_modport_expr.v:22:18: Can't find definition of 'a' in dotted variable/method: 'i.a' - 22 | assign i.b = i.a; - | ^ -%Error: t/t_interface_modport_expr.v:22:12: Can't find definition of 'b' in dotted variable/method: 'i.b' - 22 | assign i.b = i.a; - | ^ -%Error: Exiting due to diff --git a/test_regress/t/t_interface_modport_expr.py b/test_regress/t/t_interface_modport_expr.py index 2938f95de..6fe7d000c 100755 --- a/test_regress/t/t_interface_modport_expr.py +++ b/test_regress/t/t_interface_modport_expr.py @@ -4,18 +4,15 @@ # 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: 2025 Wilson Snyder +# SPDX-FileCopyrightText: 2026 Wilson Snyder # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 import vltest_bootstrap test.scenarios('simulator') -test.compile(fails=test.vlt_all, - expect_filename=test.golden_filename, - verilator_flags2=["--binary"]) +test.compile(verilator_flags2=["--binary"]) -if not test.vlt_all: - test.execute() +test.execute() test.passes() diff --git a/test_regress/t/t_interface_modport_expr.v b/test_regress/t/t_interface_modport_expr.v index 120567917..3e2f39689 100644 --- a/test_regress/t/t_interface_modport_expr.v +++ b/test_regress/t/t_interface_modport_expr.v @@ -9,11 +9,18 @@ `define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); // verilog_format: on -interface my_if; - logic sig_a, sig_b, sig_c, sig_d; +interface my_if #(parameter WIDTH = 1); + logic [WIDTH-1:0] sig_a, sig_b, sig_c, sig_d; + logic [WIDTH-1:0] sig_e, sig_f; + // Multiple expressions same direction + logic [WIDTH-1:0] m1, m2, m3; modport mp1(input .a(sig_a), output .b(sig_b)); modport mp2(input .a(sig_c), output .b(sig_d)); + // Mixed regular and expression items + modport mp3(input sig_e, output .f(sig_f)); + // Multiple expressions with same direction + modport mp4(input .in1(m1), input .in2(m2), output .out(m3)); endinterface module mod1 ( @@ -28,17 +35,35 @@ module mod2 ( assign i.b = i.a; endmodule -module top (); - my_if myIf (); - assign myIf.sig_a = 1'b1, myIf.sig_c = 1'b1; +module mod3 ( + my_if.mp3 i +); + assign i.f = i.sig_e; // sig_e is regular, f is expression +endmodule - mod1 mod1Instance (myIf); - mod2 mod2Instance (myIf); +module mod4 ( + my_if.mp4 i +); + assign i.out = i.in1 ^ i.in2; +endmodule + +module top (); + my_if #(.WIDTH(8)) myIf (); + assign myIf.sig_a = 8'h42, myIf.sig_c = 8'hAB; + assign myIf.sig_e = 8'hCD; + assign myIf.m1 = 8'hF0, myIf.m2 = 8'h0F; + + mod1 mod1i (myIf.mp1); + mod2 mod2i (myIf.mp2); + mod3 mod3i (myIf.mp3); + mod4 mod4i (myIf.mp4); initial begin #1; - `checkh(myIf.sig_a, myIf.sig_b); - `checkh(myIf.sig_c, myIf.sig_d); + `checkh(myIf.sig_b, 8'h42); // mp1: b = a + `checkh(myIf.sig_d, 8'hAB); // mp2: b = a + `checkh(myIf.sig_f, 8'hCD); // mp3: f = sig_e + `checkh(myIf.m3, 8'hFF); // mp4: out = in1 ^ in2 #1; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_interface_modport_expr_array.out b/test_regress/t/t_interface_modport_expr_array.out new file mode 100644 index 000000000..21223a469 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_array.out @@ -0,0 +1,43 @@ +%Error-UNSUPPORTED: t/t_interface_modport_expr_array.v:26:13: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4) + 26 | output .ch0_wr(ch[0].wr), input .ch0_rd(ch[0].rd), + | ^~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_interface_modport_expr_array.v:26:13: Modport item not found: 'ch0_wr' + 26 | output .ch0_wr(ch[0].wr), input .ch0_rd(ch[0].rd), + | ^~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-UNSUPPORTED: t/t_interface_modport_expr_array.v:26:38: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4) + 26 | output .ch0_wr(ch[0].wr), input .ch0_rd(ch[0].rd), + | ^~~~~~ +%Error: t/t_interface_modport_expr_array.v:26:38: Modport item not found: 'ch0_rd' + 26 | output .ch0_wr(ch[0].wr), input .ch0_rd(ch[0].rd), + | ^~~~~~ +%Error-UNSUPPORTED: t/t_interface_modport_expr_array.v:27:13: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4) + 27 | output .ch1_wr(ch[1].wr), input .ch1_rd(ch[1].rd) + | ^~~~~~ +%Error: t/t_interface_modport_expr_array.v:27:13: Modport item not found: 'ch1_wr' + 27 | output .ch1_wr(ch[1].wr), input .ch1_rd(ch[1].rd) + | ^~~~~~ +%Error-UNSUPPORTED: t/t_interface_modport_expr_array.v:27:38: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4) + 27 | output .ch1_wr(ch[1].wr), input .ch1_rd(ch[1].rd) + | ^~~~~~ +%Error: t/t_interface_modport_expr_array.v:27:38: Modport item not found: 'ch1_rd' + 27 | output .ch1_wr(ch[1].wr), input .ch1_rd(ch[1].rd) + | ^~~~~~ +%Error: t/t_interface_modport_expr_array.v:33:29: Can't find definition of 'ch0_wr' in dotted variable/method: 'port.ch0_wr' + 33 | assign port.ch0_rd = port.ch0_wr + 8'h10; + | ^~~~~~ + ... Known scopes under 'port': +%Error: t/t_interface_modport_expr_array.v:33:15: Can't find definition of 'ch0_rd' in dotted variable/method: 'port.ch0_rd' + 33 | assign port.ch0_rd = port.ch0_wr + 8'h10; + | ^~~~~~ + ... Known scopes under 'port': +%Error: t/t_interface_modport_expr_array.v:34:29: Can't find definition of 'ch1_wr' in dotted variable/method: 'port.ch1_wr' + 34 | assign port.ch1_rd = port.ch1_wr + 8'h20; + | ^~~~~~ + ... Known scopes under 'port': +%Error: t/t_interface_modport_expr_array.v:34:15: Can't find definition of 'ch1_rd' in dotted variable/method: 'port.ch1_rd' + 34 | assign port.ch1_rd = port.ch1_wr + 8'h20; + | ^~~~~~ + ... Known scopes under 'port': +%Error: Exiting due to diff --git a/test_regress/t/t_interface_modport_expr_array.py b/test_regress/t/t_interface_modport_expr_array.py new file mode 100755 index 000000000..3bacbbfc8 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_array.py @@ -0,0 +1,21 @@ +#!/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(fails=test.vlt_all, + expect_filename=test.golden_filename, + verilator_flags2=["--binary"]) + +if not test.vlt_all: + test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_modport_expr_array.v b/test_regress/t/t_interface_modport_expr_array.v new file mode 100644 index 000000000..b52a33945 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_array.v @@ -0,0 +1,51 @@ +// 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 + +// Test modport expressions with arrayed interface instances (unsupported) + +// verilog_format: off +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +interface base_if; + logic [7:0] wr; + logic [7:0] rd; + modport host(output wr, input rd); + modport dev(input wr, output rd); +endinterface + +interface container_if; + base_if ch[2](); + + // Modport expressions accessing arrayed interface instances + modport mp( + output .ch0_wr(ch[0].wr), input .ch0_rd(ch[0].rd), + output .ch1_wr(ch[1].wr), input .ch1_rd(ch[1].rd) + ); +endinterface + +module consumer(container_if.mp port); + // Access through modport expression virtual ports + assign port.ch0_rd = port.ch0_wr + 8'h10; + assign port.ch1_rd = port.ch1_wr + 8'h20; +endmodule + +module top; + container_if cont(); + + consumer m_cons(.port(cont)); + + initial begin + cont.ch[0].wr = 8'hA0; + cont.ch[1].wr = 8'hB0; + #1; + `checkh(cont.ch[0].rd, 8'hB0); + `checkh(cont.ch[1].rd, 8'hD0); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_modport_expr_bad.out b/test_regress/t/t_interface_modport_expr_bad.out new file mode 100644 index 000000000..2754d3ad8 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_bad.out @@ -0,0 +1,18 @@ +%Error: t/t_interface_modport_expr_bad.v:10:22: Can't find modport expression target: 'nonexist.sig' + 10 | modport mp1(input .in(nonexist.sig)); + | ^~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_interface_modport_expr_bad.v:10:22: Modport item not found: 'in' + 10 | modport mp1(input .in(nonexist.sig)); + | ^~ +%Error-UNSUPPORTED: t/t_interface_modport_expr_bad.v:12:22: Unsupported: Complex modport expression (IEEE 1800-2023 25.5.4) + 12 | modport mp2(input .in(~a)); + | ^~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_interface_modport_expr_bad.v:12:22: Modport item not found: 'in' + 12 | modport mp2(input .in(~a)); + | ^~ +%Error: t/t_interface_modport_expr_bad.v:10:25: Can't find definition of scope/variable: 'nonexist' + 10 | modport mp1(input .in(nonexist.sig)); + | ^~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_interface_modport_expr_bad.py b/test_regress/t/t_interface_modport_expr_bad.py new file mode 100644 index 000000000..3bacbbfc8 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_bad.py @@ -0,0 +1,21 @@ +#!/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(fails=test.vlt_all, + expect_filename=test.golden_filename, + verilator_flags2=["--binary"]) + +if not test.vlt_all: + test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_modport_expr_bad.v b/test_regress/t/t_interface_modport_expr_bad.v new file mode 100644 index 000000000..2f9c2b26f --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_bad.v @@ -0,0 +1,17 @@ +// 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 + +interface iface; + logic a, b; + // Dotted path to non-existent signal + modport mp1(input .in(nonexist.sig)); + // Complex expression not supported as modport expression + modport mp2(input .in(~a)); +endinterface + +module t; + iface intf (); +endmodule diff --git a/test_regress/t/t_interface_modport_expr_hier.py b/test_regress/t/t_interface_modport_expr_hier.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_hier.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_interface_modport_expr_hier.v b/test_regress/t/t_interface_modport_expr_hier.v new file mode 100644 index 000000000..7f371f2af --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_hier.v @@ -0,0 +1,142 @@ +// 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 + +// Test modport expressions with hierarchical module instantiation + +// verilog_format: off +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +// ============================================================ +// Scenario A: Single-level hierarchy + expression modport +// ============================================================ + +interface if_a; + logic [7:0] raw_in; + logic [7:0] raw_out; + modport mp(input .data_in(raw_in), output .data_out(raw_out)); +endinterface + +// Leaf module: uses modport expression virtual ports +module a_leaf(if_a.mp port); + assign port.data_out = port.data_in + 8'h10; +endmodule + +// Mid-level module: passes interface down +module a_mid(if_a.mp port); + a_leaf u_leaf(.port(port)); +endmodule + +// ============================================================ +// Scenario B: 2-level deep hierarchy +// ============================================================ + +interface if_b; + logic [15:0] x; + logic [15:0] y; + modport mp(input .alpha(x), output .beta(y)); +endinterface + +module b_leaf(if_b.mp port); + assign port.beta = port.alpha ^ 16'hFFFF; +endmodule + +module b_mid(if_b.mp port); + b_leaf u_leaf(.port(port)); +endmodule + +module b_top_wrap(if_b.mp port); + b_mid u_mid(.port(port)); +endmodule + +// ============================================================ +// Scenario C: Nested interface + expression modport + hierarchy +// ============================================================ + +interface if_c_inner; + logic [7:0] val; +endinterface + +interface if_c_outer; + if_c_inner inner(); + modport mp(output .w(inner.val), input .r(inner.val)); +endinterface + +module c_leaf(if_c_outer.mp port); + assign port.w = 8'hBE; + wire [7:0] c_read = port.r; +endmodule + +module c_mid(if_c_outer.mp port); + c_leaf u_leaf(.port(port)); +endmodule + +// ============================================================ +// Scenario D: Multiple instances of same wrapper (no cross-talk) +// ============================================================ + +interface if_d; + logic [7:0] din; + logic [7:0] dout; + modport mp(input .vi(din), output .vo(dout)); +endinterface + +module d_leaf(if_d.mp port); + assign port.vo = port.vi + 8'h01; +endmodule + +module d_mid(if_d.mp port); + d_leaf u_leaf(.port(port)); +endmodule + +// ============================================================ +// Top module +// ============================================================ + +module top; + + // --- Scenario A --- + if_a ifa(); + a_mid u_a(.port(ifa)); + + // --- Scenario B --- + if_b ifb(); + b_top_wrap u_b(.port(ifb)); + + // --- Scenario C --- + if_c_outer ifc(); + c_mid u_c(.port(ifc)); + + // --- Scenario D --- + if_d ifd(); + d_mid u_d(.port(ifd)); + + initial begin + // Scenario A: single-level hierarchy + ifa.raw_in = 8'h20; + #1; + `checkh(ifa.raw_out, 8'h30); + + // Scenario B: 2-level deep hierarchy + ifb.x = 16'h1234; + #1; + `checkh(ifb.y, 16'hEDCB); + + // Scenario C: nested interface + hierarchy + #1; + `checkh(ifc.inner.val, 8'hBE); + `checkh(u_c.u_leaf.c_read, 8'hBE); + + // Scenario D: single instance through hierarchy + ifd.din = 8'h10; + #1; + `checkh(ifd.dout, 8'h11); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_modport_expr_hier_noinl.py b/test_regress/t/t_interface_modport_expr_hier_noinl.py new file mode 100755 index 000000000..092cc4a23 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_hier_noinl.py @@ -0,0 +1,19 @@ +#!/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.top_filename = "t/t_interface_modport_expr_hier.v" + +test.compile(verilator_flags2=["--binary", "-fno-inline"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_modport_expr_nested.py b/test_regress/t/t_interface_modport_expr_nested.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_nested.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_interface_modport_expr_nested.v b/test_regress/t/t_interface_modport_expr_nested.v new file mode 100644 index 000000000..babb3acf8 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_nested.v @@ -0,0 +1,130 @@ +// 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 + +// Test modport expressions with nested interfaces + +// verilog_format: off +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +interface base_reg_if; + logic [7:0] wr; + logic [7:0] rd; + modport host(output wr, input rd); + modport dev(input wr, output rd); +endinterface + +interface example_reg_if; + logic [15:0] wr; + logic [15:0] rd; + modport host(output wr, input rd); + modport dev(input wr, output rd); +endinterface + +interface app_reg_if; + base_reg_if base(); + example_reg_if example(); + + // Use modport expressions to expose nested interface signals + modport host( + output .base_wr(base.wr), input .base_rd(base.rd), + output .example_wr(example.wr), input .example_rd(example.rd) + ); + modport dev( + input .base_wr(base.wr), output .base_rd(base.rd), + input .example_wr(example.wr), output .example_rd(example.rd) + ); +endinterface + +// Deep nesting test (3 levels) +interface inner_if; + logic [7:0] data; + modport producer(output data); + modport consumer(input data); +endinterface + +interface middle_if; + inner_if inner(); +endinterface + +interface outer_if; + middle_if middle(); + + // 3-level deep modport expression + modport mp( + output .deep_out(middle.inner.data), + input .deep_in(middle.inner.data) + ); +endinterface + +module deep_consumer(outer_if.mp port); + assign port.deep_out = 8'hDE; + // Verify reading through deep_in virtual port + wire [7:0] deep_in_val = port.deep_in; +endmodule + +// 4-level deep nesting test +interface level1_if; + logic [7:0] val; +endinterface + +interface level2_if; + level1_if l1(); +endinterface + +interface level3_if; + level2_if l2(); +endinterface + +interface level4_if; + level3_if l3(); + + // 4-level deep modport expression + modport mp( + output .deep4_w(l3.l2.l1.val), + input .deep4_r(l3.l2.l1.val) + ); +endinterface + +module deep4_consumer(level4_if.mp port); + assign port.deep4_w = 8'h4D; + wire [7:0] deep4_read = port.deep4_r; +endmodule + +module app_consumer ( + app_reg_if.dev i_app_regs +); + // Access through modport expression virtual ports + assign i_app_regs.base_rd = i_app_regs.base_wr + 8'h1; + assign i_app_regs.example_rd = i_app_regs.example_wr + 16'h1; +endmodule + +module top; + app_reg_if app_regs(); + outer_if outer(); + level4_if lev4(); + + app_consumer m_app(.i_app_regs(app_regs)); + deep_consumer m_deep(.port(outer)); + deep4_consumer m_deep4(.port(lev4)); + + initial begin + app_regs.base.wr = 8'hAB; + app_regs.example.wr = 16'hCDEF; + #1; + `checkh(app_regs.base.rd, 8'hAC); + `checkh(app_regs.example.rd, 16'hCDF0); + // Verify 3-level deep nesting + `checkh(outer.middle.inner.data, 8'hDE); + `checkh(m_deep.deep_in_val, 8'hDE); + // Verify 4-level deep nesting + `checkh(lev4.l3.l2.l1.val, 8'h4D); + `checkh(m_deep4.deep4_read, 8'h4D); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_interface_modport_expr_nested_noinl.py b/test_regress/t/t_interface_modport_expr_nested_noinl.py new file mode 100755 index 000000000..c8b950dc5 --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_nested_noinl.py @@ -0,0 +1,19 @@ +#!/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.top_filename = "t/t_interface_modport_expr_nested.v" + +test.compile(verilator_flags2=["--binary", "-fno-inline"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_modport_expr_noinl.py b/test_regress/t/t_interface_modport_expr_noinl.py new file mode 100755 index 000000000..63982213e --- /dev/null +++ b/test_regress/t/t_interface_modport_expr_noinl.py @@ -0,0 +1,19 @@ +#!/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.top_filename = "t/t_interface_modport_expr.v" + +test.compile(verilator_flags2=["--binary", "-fno-inline"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_interface_modport_expr_partsel.out b/test_regress/t/t_interface_modport_expr_partsel.out index deb4ee46c..8ebfe6774 100644 --- a/test_regress/t/t_interface_modport_expr_partsel.out +++ b/test_regress/t/t_interface_modport_expr_partsel.out @@ -1,4 +1,4 @@ -%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:16:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) +%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:16:22: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4) 16 | modport mp1(input .in(a[7:0]), output .out(b)); | ^~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest @@ -6,34 +6,18 @@ 16 | modport mp1(input .in(a[7:0]), output .out(b)); | ^~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:16:42: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) - 16 | modport mp1(input .in(a[7:0]), output .out(b)); - | ^~~ -%Error: t/t_interface_modport_expr_partsel.v:16:42: Modport item not found: 'out' - 16 | modport mp1(input .in(a[7:0]), output .out(b)); - | ^~~ -%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:17:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) +%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:17:22: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4) 17 | modport mp2(input .in(a[15:8]), output .out(c)); | ^~ %Error: t/t_interface_modport_expr_partsel.v:17:22: Modport item not found: 'in' 17 | modport mp2(input .in(a[15:8]), output .out(c)); | ^~ -%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:17:43: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4) - 17 | modport mp2(input .in(a[15:8]), output .out(c)); - | ^~~ -%Error: t/t_interface_modport_expr_partsel.v:17:43: Modport item not found: 'out' - 17 | modport mp2(input .in(a[15:8]), output .out(c)); - | ^~~ %Error: t/t_interface_modport_expr_partsel.v:29:21: Can't find definition of 'in' in dotted variable/method: 'i.in' 29 | assign i.out = ~i.in; | ^~ -%Error: t/t_interface_modport_expr_partsel.v:29:12: Can't find definition of 'out' in dotted variable/method: 'i.out' - 29 | assign i.out = ~i.in; - | ^~~ + ... Known scopes under 'i': %Error: t/t_interface_modport_expr_partsel.v:23:20: Can't find definition of 'in' in dotted variable/method: 'i.in' 23 | assign i.out = i.in; | ^~ -%Error: t/t_interface_modport_expr_partsel.v:23:12: Can't find definition of 'out' in dotted variable/method: 'i.out' - 23 | assign i.out = i.in; - | ^~~ + ... Known scopes under 'i': %Error: Exiting due to diff --git a/test_regress/t/t_interface_modport_expr_partsel.py b/test_regress/t/t_interface_modport_expr_partsel.py index 2938f95de..3bacbbfc8 100755 --- a/test_regress/t/t_interface_modport_expr_partsel.py +++ b/test_regress/t/t_interface_modport_expr_partsel.py @@ -4,7 +4,7 @@ # 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: 2025 Wilson Snyder +# SPDX-FileCopyrightText: 2026 Wilson Snyder # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 import vltest_bootstrap diff --git a/test_regress/t/t_mod_interface_clocking_bad.out b/test_regress/t/t_mod_interface_clocking_bad.out index 017459e00..7bc404203 100644 --- a/test_regress/t/t_mod_interface_clocking_bad.out +++ b/test_regress/t/t_mod_interface_clocking_bad.out @@ -5,7 +5,8 @@ %Error: t/t_mod_interface_clocking_bad.v:16:41: Modport item not found: 'cx' 16 | modport mp(input clk, clocking reset, clocking cx); | ^~~~~~~~ -%Error: t/t_mod_interface_clocking_bad.v:25:10: Can't find definition of 'cb' +%Error: t/t_mod_interface_clocking_bad.v:25:7: Can't find definition of 'cb' in dotted scope/variable: 'x.cb' 25 | x.cb.reset <= 1; - | ^~~~~ + | ^~ + ... Known scopes under 'x': %Error: Exiting due to