diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 27881e00a..210c55f7a 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1024,19 +1024,18 @@ class AstDefParam final : public AstNode { // A defparam assignment // Parents: MODULE // @astgen op1 := rhsp : AstNodeExpr + // @astgen op2 := pathp : AstNodeExpr string m_name; // Name of variable getting set - string m_path; // Dotted cellname to set parameter of public: - AstDefParam(FileLine* fl, const string& path, const string& name, AstNodeExpr* rhsp) + AstDefParam(FileLine* fl, AstNodeExpr* pathp, const string& name, AstNodeExpr* rhsp) : ASTGEN_SUPER_DefParam(fl) - , m_name{name} - , m_path{path} { + , m_name{name} { this->rhsp(rhsp); + this->pathp(pathp); } string name() const override VL_MT_STABLE { return m_name; } // * = Scope name ASTGEN_MEMBERS_AstDefParam; bool sameNode(const AstNode*) const override { return true; } - string path() const { return m_path; } }; class AstDefaultDisable final : public AstNode { // @astgen op1 := condp : AstNodeExpr @@ -1390,6 +1389,7 @@ class AstPin final : public AstNode { // @astgen ptr := m_modPTypep : Optional[AstParamTypeDType] // Param type connects to on sub int m_pinNum; // Pin number string m_name; // Pin name, or "" for number based interconnect + string m_paramPath; // Original defparam cell path, if this pin came from a defparam bool m_param = false; // Pin connects to parameter bool m_svDotName = false; // Pin is SystemVerilog .name'ed bool m_svImplicit = false; // Pin is SystemVerilog .name'ed, allow implicit @@ -1416,6 +1416,8 @@ public: void modPTypep(AstParamTypeDType* nodep) { m_modPTypep = nodep; } bool param() const { return m_param; } void param(bool flag) { m_param = flag; } + const string& paramPath() const { return m_paramPath; } + void paramPath(const string& path) { m_paramPath = path; } bool svDotName() const { return m_svDotName; } void svDotName(bool flag) { m_svDotName = flag; } bool svImplicit() const { return m_svImplicit; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 95e18091e..417ffa5b6 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2312,12 +2312,14 @@ void AstPin::dump(std::ostream& str) const { } else { str << " ->UNLINKED"; } + if (!paramPath().empty()) str << " paramPath=" << paramPath(); if (svDotName()) str << " [.n]"; if (svImplicit()) str << " [.SV]"; } void AstPin::dumpJson(std::ostream& str) const { dumpJsonBoolFuncIf(str, svDotName); dumpJsonBoolFuncIf(str, svImplicit); + if (!paramPath().empty()) dumpJsonStr(str, "paramPath", paramPath()); dumpJsonGen(str); } string AstPin::prettyOperatorName() const { diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 21b5e6fba..9a39a4afd 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -80,6 +80,23 @@ VL_DEFINE_DEBUG_FUNCTIONS; +static 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); + return VString::dot(lhs, ".", rhs); + } else if (VN_IS(nodep, SelBit) || VN_IS(nodep, SelExtract) || VN_IS(nodep, SelPlus) + || VN_IS(nodep, SelMinus)) { + hasPartSelect = true; + return ""; + } + return ""; +} + // ###################################################################### // Matcher classes (for suggestion matching) @@ -2401,19 +2418,26 @@ class LinkDotParamVisitor final : public VNVisitor { << nodep->warnMore() << "... Suggest use instantiation with #(." << nodep->prettyName() << "(...etc...))"); - VSymEnt* const foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->path()); + bool hasPartSelect = false; + const string path = extractDottedPath(nodep->pathp(), hasPartSelect); + UASSERT_OBJ(!hasPartSelect && !path.empty(), nodep, "Unexpected defparam path shape"); + string baddot; + VSymEnt* okSymp = nullptr; + VSymEnt* const foundp = m_statep->findDotted( + nodep->fileline(), m_statep->getNodeSym(nodep), path, baddot, okSymp, true); AstCell* const cellp = foundp ? VN_AS(foundp->nodep(), Cell) : nullptr; if (!cellp) { - nodep->v3error("In defparam, instance " << nodep->path() << " never declared"); + nodep->v3error("In defparam, instance " << path << " never declared"); } else { AstNodeExpr* const exprp = nodep->rhsp()->unlinkFrBack(); - UINFO(9, "Defparam cell " << nodep->path() << "." << nodep->name() << " attach-to " - << cellp << " <= " << exprp); + UINFO(9, "Defparam cell " << path << "." << nodep->name() << " attach-to " << cellp + << " <= " << exprp); // Don't need to check the name of the defparam exists. V3Param does. AstPin* const pinp = new AstPin{nodep->fileline(), -1, // Pin# not relevant nodep->name(), exprp}; pinp->param(true); + pinp->paramPath(path); cellp->addParamsp(pinp); } VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); @@ -2797,26 +2821,6 @@ class LinkDotIfaceVisitor final : public VNVisitor { } } - // 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. @@ -3080,6 +3084,8 @@ class LinkDotResolveVisitor final : public VNVisitor { bool m_insideClassExtParam = false; // Inside a class from m_extendsParam AstNew* m_explicitSuperNewp = nullptr; // Hit a "super.new" call inside a "new" function std::map m_usedPins; // Pin used in this cell, map to duplicate + std::map> m_usedDefParamPins; + // Defparam pins used for a given formal, by hierarchical path std::map m_modulesToRevisit; // Modules to revisit a second time AstNode* m_lastDeferredp = nullptr; // Last node which requested a revisit of its module AstNodeDType* m_packedArrayDtp = nullptr; // Datatype reference for packed array @@ -3313,16 +3319,34 @@ class LinkDotResolveVisitor final : public VNVisitor { } return foundNodep; } + void duplicatePinError(AstPin* nodep, AstNode* origp, const char* whatp) { + nodep->v3error("Duplicate " << whatp << " connection: " << nodep->prettyNameQ() << '\n' + << nodep->warnContextPrimary() << '\n' + << origp->warnOther() << "... Location of original " << whatp + << " connection\n" + << origp->warnContextSecondary()); + } void markAndCheckPinDup(AstPin* nodep, AstNode* refp, const char* whatp) { const auto pair = m_usedPins.emplace(refp, nodep); - if (!pair.second) { + if (pair.second) { + if (!nodep->paramPath().empty()) + m_usedDefParamPins[refp].emplace(nodep->paramPath(), nodep); + return; + } else { AstNode* const origp = pair.first->second; - nodep->v3error("Duplicate " << whatp << " connection: " << nodep->prettyNameQ() << '\n' - << nodep->warnContextPrimary() << '\n' - << origp->warnOther() << "... Location of original " - << whatp << " connection\n" - << origp->warnContextSecondary()); + if (nodep->paramPath().empty() || VN_AS(origp, Pin)->paramPath().empty()) { + duplicatePinError(nodep, origp, whatp); + return; + } } + + auto& defParamPins = m_usedDefParamPins[refp]; + const auto defParamIt = defParamPins.find(nodep->paramPath()); + if (defParamIt != defParamPins.end()) { + duplicatePinError(nodep, defParamIt->second, whatp); + return; + } + defParamPins.emplace(nodep->paramPath(), nodep); } VSymEnt* getCreateClockingEventSymEnt(AstClocking* clockingp) { AstVar* const eventp = clockingp->ensureEventp(true); @@ -3698,7 +3722,9 @@ class LinkDotResolveVisitor final : public VNVisitor { UINFO(5, indent() << "visit " << nodep); checkNoDot(nodep); VL_RESTORER(m_usedPins); + VL_RESTORER(m_usedDefParamPins); m_usedPins.clear(); + m_usedDefParamPins.clear(); UASSERT_OBJ(nodep->modp(), nodep, "Instance has unlinked module"); // V3LinkCell should have errored out VL_RESTORER(m_cellp); @@ -3736,7 +3762,9 @@ class LinkDotResolveVisitor final : public VNVisitor { UINFO(5, indent() << "visit " << nodep); // Can be under dot if called as package::class and that class resolves, so no checkNoDot VL_RESTORER(m_usedPins); + VL_RESTORER(m_usedDefParamPins); m_usedPins.clear(); + m_usedDefParamPins.clear(); UASSERT_OBJ(nodep->classp(), nodep, "ClassRef has unlinked class"); UASSERT_OBJ(m_statep->forPrimary() || !nodep->paramsp() || V3Error::errorCount(), nodep, "class reference parameter not removed by V3Param"); @@ -4620,7 +4648,9 @@ class LinkDotResolveVisitor final : public VNVisitor { UINFO(8, indent() << "visit " << nodep); UINFO(9, indent() << m_ds.ascii()); VL_RESTORER(m_usedPins); + VL_RESTORER(m_usedDefParamPins); m_usedPins.clear(); + m_usedDefParamPins.clear(); UASSERT_OBJ(m_statep->forPrimary() || !nodep->paramsp(), nodep, "class reference parameter not removed by V3Param"); { @@ -6036,7 +6066,9 @@ class LinkDotResolveVisitor final : public VNVisitor { if (ifacep->dead()) return; checkNoDot(nodep); VL_RESTORER(m_usedPins); + VL_RESTORER(m_usedDefParamPins); m_usedPins.clear(); + m_usedDefParamPins.clear(); VL_RESTORER(m_pinSymp); m_pinSymp = m_statep->getNodeSym(ifacep); iterateAndNextNull(nodep->paramsp()); diff --git a/src/V3Param.cpp b/src/V3Param.cpp index fbb70ebce..36bd6d2d1 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -530,6 +530,32 @@ class ParamProcessor final { } } } + static bool hasDescendantDefparams(const AstNodeModule* modp, + std::set& visited) { + if (!visited.insert(modp).second) return false; + for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + AstCell* const cellp = VN_CAST(stmtp, Cell); + if (!cellp) continue; + for (AstPin* pinp = cellp->paramsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + if (!pinp->paramPath().empty()) { + visited.erase(modp); + return true; + } + } + if (AstNodeModule* const childModp = cellp->modp()) { + if (hasDescendantDefparams(childModp, visited)) { + visited.erase(modp); + return true; + } + } + } + visited.erase(modp); + return false; + } + static bool hasDescendantDefparams(const AstNodeModule* modp) { + std::set visited; + return hasDescendantDefparams(modp, visited); + } // Check if parameter setting during instantiation is simple enough for hierarchical Verilation void checkSupportedParam(AstNodeModule* modp, AstPin* pinp) const { // InitArray is not supported because that can not be set via -G @@ -1609,6 +1635,10 @@ class ParamProcessor final { } } } + if (!any_overrides && VN_IS(nodep, Cell) && hasDescendantDefparams(srcModp)) { + longname += "__Vdefparam" + V3Hash{srcModp->someInstanceName()}.toString(); + any_overrides = true; + } UINFO(9, "nodeDeparamCommon: " << srcModp->prettyNameQ() << " overrides=" << any_overrides << endl); @@ -1825,6 +1855,23 @@ public: << " parentSomeInstanceName='" << (modp ? modp->someInstanceName() : string("")) << "'" << " inputSomeInstanceName='" << someInstanceName << "'" << endl); + string nodeName = nodep->name(); + if (AstIfaceRefDType* const ifaceRefp = VN_CAST(nodep, IfaceRefDType)) { + if (nodeName.empty()) nodeName = ifaceRefp->cellName(); + } + const string instanceName + = nodeName.empty() ? someInstanceName : (someInstanceName + "." + nodeName); + + if (AstCell* const cellp = VN_CAST(nodep, Cell)) { + for (AstPin* pinp = cellp->paramsp(); pinp;) { + AstPin* const nextp = VN_AS(pinp->nextp(), Pin); + if (!pinp->paramPath().empty() && instanceName != pinp->paramPath() + && !VString::endsWith(instanceName, "." + pinp->paramPath())) { + VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp); + } + pinp = nextp; + } + } // Create new module name with _'s between the constants UINFOTREE(10, nodep, "", "cell"); // Evaluate all module constants @@ -1834,12 +1881,6 @@ public: // so use cellName() which is the actual cell instance name. // If both are empty (interface port, not a cell), skip appending // to avoid double-dots in the path. - string nodeName = nodep->name(); - if (AstIfaceRefDType* const ifaceRefp = VN_CAST(nodep, IfaceRefDType)) { - if (nodeName.empty()) nodeName = ifaceRefp->cellName(); - } - const string instanceName - = nodeName.empty() ? someInstanceName : (someInstanceName + "." + nodeName); srcModp->someInstanceName(instanceName); UINFO(9, "nodeDeparam SET-SRC-INST srcMod=" << srcModp->prettyNameQ() << " someInstanceName='" diff --git a/src/verilog.y b/src/verilog.y index 68193b70c..bb23f452c 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3215,19 +3215,26 @@ list_of_defparam_assignments: //== IEEE: list_of_defparam_assignments ; defparam_assignment: // ==IEEE: defparam_assignment - defparamIdRange '.' defparamIdRange '=' expr - { $$ = new AstDefParam{$4, *$1, *$3, $5}; } + defparamIdRangeList '.' defparamIdRange '=' expr + { $$ = new AstDefParam{$4, $1, *$3, $5}; } | defparamIdRange '=' expr { $$ = nullptr; BBUNSUP($2, "Unsupported: defparam with no dot"); DEL($3); } - | defparamIdRange '.' defparamIdRange '.' defparamIdRangeList '=' expr - { $$ = nullptr; BBUNSUP($4, "Unsupported: defparam with more than one dot"); - DEL($7); } ; -defparamIdRangeList: // IEEE: part of defparam_assignment - defparamIdRange { $$ = $1; } - | defparamIdRangeList '.' defparamIdRange { $$ = $3; } +defparamIdRangeList: // IEEE: part of defparam_assignment + defparamIdRangeExpr { $$ = $1; } + | defparamIdRangeList '.' defparamIdRangeExpr + { $$ = new AstDot{$2, false, $1, $3}; } + ; + +defparamIdRangeExpr: // IEEE: part of defparam_assignment + idAny + { $$ = new AstParseRef{$1, *$1, nullptr, nullptr}; } + | idAny part_select_rangeList + { $$ = new AstParseRef{$1, *$1, nullptr, nullptr}; + BBUNSUP($2, "Unsupported: defparam with arrayed instance"); + DEL($2); } ; defparamIdRange: // IEEE: part of defparam_assignment diff --git a/test_regress/t/t_defparam_hier.py b/test_regress/t/t_defparam_hier.py new file mode 100755 index 000000000..390bba1b8 --- /dev/null +++ b/test_regress/t/t_defparam_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(timing_loop=True, verilator_flags2=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_defparam_hier.v b/test_regress/t/t_defparam_hier.v new file mode 100644 index 000000000..bb04cde23 --- /dev/null +++ b/test_regress/t/t_defparam_hier.v @@ -0,0 +1,77 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +`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); + +module M0 #(parameter PRMTR = 1) ( + output int value +); + assign value = PRMTR; +endmodule + +module M1 #(parameter PRMTR = 1)( + output int value +); + int v0, v1; + M0 m0a(.value(v0)); + M0 m0b(.value(v1)); + assign value = v0 + v1; +endmodule + +module M2 #(parameter PRMTR = 1)( + output int value +); + M1 m1(.value(value)); +endmodule + +module M3 #(parameter PRMTR = 1)( + output int value +); + int v0, v1; + M2 m2a(.value(v0)); + M2 m2b(.value(v1)); + assign value = v0 * v1; +endmodule + +module top; + int value; + + M3 m3(.value(value)); + + defparam m3.m2a.m1.m0a.PRMTR = 2; + defparam m3.m2a.m1.m0b.PRMTR = 3; + defparam m3.m2b.m1.m0a.PRMTR = 4; + defparam m3.m2b.m1.m0b.PRMTR = 5; + + defparam m3.m2a.m1.PRMTR = 6; + defparam m3.m2b.m1.PRMTR = 7; + + defparam m3.m2a.PRMTR = 8; + defparam m3.m2b.PRMTR = 9; + + defparam m3.PRMTR = 10; + + initial begin + `checkh(m3.m2a.m1.m0a.PRMTR, 2); + `checkh(m3.m2a.m1.m0b.PRMTR, 3); + `checkh(m3.m2b.m1.m0a.PRMTR, 4); + `checkh(m3.m2b.m1.m0b.PRMTR, 5); + + `checkh(m3.m2a.m1.PRMTR, 6); + `checkh(m3.m2b.m1.PRMTR, 7); + + `checkh(m3.m2a.PRMTR, 8); + `checkh(m3.m2b.PRMTR, 9); + + `checkh(m3.PRMTR, 10); + + #1; + `checkh(value, 45); // (2+3) * (4+5) + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_gen_defparam_multi.out b/test_regress/t/t_gen_defparam_multi.out index 978c2c93a..bf1628dd4 100644 --- a/test_regress/t/t_gen_defparam_multi.out +++ b/test_regress/t/t_gen_defparam_multi.out @@ -5,13 +5,4 @@ %Error-UNSUPPORTED: t/t_gen_defparam_multi.v:28:19: Unsupported: defparam with arrayed instance 28 | defparam blk[i].u_m3.PAR3 = i; | ^ -%Error-UNSUPPORTED: t/t_gen_defparam_multi.v:28:27: Unsupported: defparam with more than one dot - 28 | defparam blk[i].u_m3.PAR3 = i; - | ^ -%Error-UNSUPPORTED: t/t_gen_defparam_multi.v:51:43: Unsupported: defparam with more than one dot - 51 | defparam m2.PAR2 = 8; defparam m2.m3.PAR3 = 80; - | ^ -%Error-UNSUPPORTED: t/t_gen_defparam_multi.v:55:43: Unsupported: defparam with more than one dot - 55 | defparam m2.PAR2 = 4; defparam m2.m3.PAR3 = 40; - | ^ %Error: Exiting due to diff --git a/test_regress/t/t_lint_pindup_bad.out b/test_regress/t/t_lint_pindup_bad.out index b1c04b183..e6634e7f4 100644 --- a/test_regress/t/t_lint_pindup_bad.out +++ b/test_regress/t/t_lint_pindup_bad.out @@ -1,24 +1,27 @@ %Warning-PINMISSING: t/t_lint_pindup_bad.v:18:7: Instance has missing pin: 'exists' 18 | sub ( | ^~~ - t/t_lint_pindup_bad.v:33:16: ... Location of port declaration - 33 | input wire exists + t/t_lint_pindup_bad.v:37:16: ... Location of port declaration + 37 | input wire exists | ^~~~~~ ... For warning description see https://verilator.org/warn/PINMISSING?v=latest ... Use "/* verilator lint_off PINMISSING */" and lint_on around source to disable this message. +%Error: t/t_lint_pindup_bad.v:26:30: In defparam, instance sub.subnotfound never declared + 26 | defparam sub.subnotfound.P = 2; + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: t/t_lint_pindup_bad.v:21:8: Duplicate pin connection: 'i' 21 | .i(i2), | ^ t/t_lint_pindup_bad.v:20:8: ... Location of original pin connection 20 | .i(i), | ^ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error-PINNOTFOUND: t/t_lint_pindup_bad.v:22:8: Pin not found: 'nexist' : ... Suggested alternative: 'exists' 22 | .nexist(i2) | ^~~~~~ : ... Location of instance's module declaration - 27 | module sub #( + 31 | module sub #( | ^~~ ... For error description see https://verilator.org/warn/PINNOTFOUND?v=latest %Error-PINNOTFOUND: t/t_lint_pindup_bad.v:14:8: Parameter not found: 'NEXIST' @@ -26,7 +29,7 @@ 14 | .NEXIST(1), | ^~~~~~ : ... Location of instance's module declaration - 27 | module sub #( + 31 | module sub #( | ^~~ %Error: t/t_lint_pindup_bad.v:16:8: Duplicate parameter connection: 'P' 16 | .P(3) @@ -34,4 +37,17 @@ t/t_lint_pindup_bad.v:15:8: ... Location of original parameter connection 15 | .P(2), | ^ +%Error: t/t_lint_pindup_bad.v:25:18: Duplicate parameter connection: 'P' + 25 | defparam sub.P = 2; + | ^ + t/t_lint_pindup_bad.v:15:8: ... Location of original parameter connection + 15 | .P(2), + | ^ +%Error-PINNOTFOUND: t/t_lint_pindup_bad.v:27:23: Parameter not found: 'NEXIST' + : ... Suggested alternative: 'EXIST' + 27 | defparam sub.NEXIST = 2; + | ^ + : ... Location of instance's module declaration + 31 | module sub #( + | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_lint_pindup_bad.v b/test_regress/t/t_lint_pindup_bad.v index 1112f6d56..686822955 100644 --- a/test_regress/t/t_lint_pindup_bad.v +++ b/test_regress/t/t_lint_pindup_bad.v @@ -22,6 +22,10 @@ module t ( .nexist(i2) // Not found ); + defparam sub.P = 2; // Dup + defparam sub.subnotfound.P = 2; // Not found + defparam sub.NEXIST = 2; // Not found + endmodule module sub #(