From c2cba8bfc6fa62fa455c95c2d917e4fc5180a513 Mon Sep 17 00:00:00 2001 From: em2machine <92717390+em2machine@users.noreply.github.com> Date: Sat, 6 Dec 2025 08:42:59 -0600 Subject: [PATCH] Fix localparam type assignment from interface type parameters (#6637) (#6732) --- docs/CONTRIBUTORS | 1 + src/CMakeLists.txt | 2 + src/Makefile_obj.in | 1 + src/V3LinkDot.cpp | 422 +++++++++++++++--- src/V3LinkDotIfaceCapture.cpp | 218 +++++++++ src/V3LinkDotIfaceCapture.h | 90 ++++ src/V3Param.cpp | 80 +++- src/verilog.y | 10 +- .../t/t_lparam_assign_iface_array_typedef.py | 18 + .../t/t_lparam_assign_iface_array_typedef.v | 69 +++ .../t/t_lparam_assign_iface_array_typedef2.py | 18 + .../t/t_lparam_assign_iface_array_typedef2.v | 69 +++ .../t/t_lparam_assign_iface_typedef.py | 18 + .../t/t_lparam_assign_iface_typedef.v | 59 +++ .../t/t_lparam_assign_iface_typedef2.py | 18 + .../t/t_lparam_assign_iface_typedef2.v | 29 ++ .../t/t_lparam_assign_iface_typedef3.py | 18 + .../t/t_lparam_assign_iface_typedef3.v | 60 +++ .../t/t_lparam_assign_iface_typedef_nested.py | 18 + .../t/t_lparam_assign_iface_typedef_nested.v | 47 ++ .../t_lparam_assign_iface_typedef_nested2.py | 18 + .../t/t_lparam_assign_iface_typedef_nested2.v | 61 +++ .../t_lparam_assign_iface_typedef_nested3.py | 18 + .../t/t_lparam_assign_iface_typedef_nested3.v | 74 +++ .../t_lparam_assign_iface_typedef_nested4.py | 18 + .../t/t_lparam_assign_iface_typedef_nested4.v | 70 +++ .../t_lparam_assign_iface_typedef_nested5.py | 18 + .../t/t_lparam_assign_iface_typedef_nested5.v | 86 ++++ ...ram_assign_iface_typedef_nested_modules.py | 18 + ...aram_assign_iface_typedef_nested_modules.v | 38 ++ ...am_assign_iface_typedef_nested_modules2.py | 18 + ...ram_assign_iface_typedef_nested_modules2.v | 63 +++ ...am_assign_iface_typedef_nested_modules3.py | 18 + ...ram_assign_iface_typedef_nested_modules3.v | 70 +++ ...assign_iface_typedef_nested_modules_pkg.py | 18 + ..._assign_iface_typedef_nested_modules_pkg.v | 76 ++++ ...ssign_iface_typedef_nested_modules_pkg2.py | 18 + ...assign_iface_typedef_nested_modules_pkg2.v | 39 ++ ...ssign_iface_typedef_nested_modules_pkg3.py | 18 + ...assign_iface_typedef_nested_modules_pkg3.v | 45 ++ ...ssign_iface_typedef_nested_modules_pkg4.py | 18 + ...assign_iface_typedef_nested_modules_pkg4.v | 57 +++ ...ssign_iface_typedef_nested_modules_pkg5.py | 18 + ...assign_iface_typedef_nested_modules_pkg5.v | 55 +++ ..._lparam_assign_iface_typedef_nested_pkg.py | 18 + ...t_lparam_assign_iface_typedef_nested_pkg.v | 106 +++++ ...lparam_assign_iface_typedef_nested_pkg2.py | 18 + ..._lparam_assign_iface_typedef_nested_pkg2.v | 96 ++++ ...lparam_assign_iface_typedef_nested_pkg3.py | 18 + ..._lparam_assign_iface_typedef_nested_pkg3.v | 69 +++ test_regress/t/t_param_type_bad.out | 2 +- test_regress/t/t_param_type_bad3.out | 2 +- test_regress/t/t_param_type_id_bad.out | 2 +- test_regress/t/t_typedef_id_bad.out | 2 +- 54 files changed, 2469 insertions(+), 79 deletions(-) create mode 100644 src/V3LinkDotIfaceCapture.cpp create mode 100644 src/V3LinkDotIfaceCapture.h create mode 100755 test_regress/t/t_lparam_assign_iface_array_typedef.py create mode 100644 test_regress/t/t_lparam_assign_iface_array_typedef.v create mode 100755 test_regress/t/t_lparam_assign_iface_array_typedef2.py create mode 100644 test_regress/t/t_lparam_assign_iface_array_typedef2.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef2.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef2.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef3.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef3.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested2.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested2.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested3.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested3.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested4.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested4.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested5.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested5.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_modules.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_modules.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_modules2.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_modules2.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_modules3.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_modules3.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg2.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg2.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg3.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg3.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg4.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg4.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg5.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg5.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_pkg.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_pkg.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_pkg2.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_pkg2.v create mode 100755 test_regress/t/t_lparam_assign_iface_typedef_nested_pkg3.py create mode 100644 test_regress/t/t_lparam_assign_iface_typedef_nested_pkg3.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 832bbbece..32044c117 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -276,5 +276,6 @@ Zixi Li dependabot[bot] february cozzocrea sumpster +em2machine Àlex Torregrosa Ícaro Lima diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 98a125bfa..3bb4808c1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -123,6 +123,7 @@ set(HEADERS V3LifePost.h V3LinkCells.h V3LinkDot.h + V3LinkDotIfaceCapture.h V3LinkInc.h V3LinkJump.h V3LinkLValue.h @@ -293,6 +294,7 @@ set(COMMON_SOURCES V3LifePost.cpp V3LinkCells.cpp V3LinkDot.cpp + V3LinkDotIfaceCapture.cpp V3LinkInc.cpp V3LinkJump.cpp V3LinkLValue.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index d7b679c05..78631a4fc 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -291,6 +291,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3LifePost.o \ V3LinkCells.o \ V3LinkDot.o \ + V3LinkDotIfaceCapture.o \ V3LinkInc.o \ V3LinkJump.o \ V3LinkLValue.o \ diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index c1c996d4d..9542e7c2f 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -67,12 +67,15 @@ #include "V3Global.h" #include "V3Graph.h" +#include "V3LinkDotIfaceCapture.h" #include "V3MemberMap.h" #include "V3Parse.h" #include "V3Randomize.h" #include "V3String.h" #include "V3SymTable.h" +#include +#include #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -217,9 +220,31 @@ public: UINFO(4, __FUNCTION__ << ": "); s_errorThisp = this; V3Error::errorExitCb(preErrorDumpHandler); // If get error, dump self + const std::size_t capturedCount = V3LinkDotIfaceCapture::size(); + if (forPrimary()) { + V3LinkDotIfaceCapture::enable(true); + UINFO(9, "iface capture enabled for primary pass (persisting entries) size=" + << capturedCount); + } else if (forParamed()) { + UINFO(9, + "iface capture entering paramed pass captured typedef count=" << capturedCount); + } readModNames(); } ~LinkDotState() { + const std::size_t capturedCount = V3LinkDotIfaceCapture::size(); + if (forPrimary()) { + UINFO(9, + "iface capture leaving primary pass captured typedef count=" << capturedCount); + } else if (forParamed()) { + UINFO(9, + "iface capture leaving paramed pass captured typedef count=" << capturedCount); + if (capturedCount != 0) { + UINFO(9, "iface capture warning: leftover captured typedef entries=" + << capturedCount); + } + V3LinkDotIfaceCapture::reset(); + } V3Error::errorExitCb(nullptr); s_errorThisp = nullptr; } @@ -815,32 +840,31 @@ public: } static bool checkIfClassOrPackage(const VSymEnt* const symp) { if (VN_IS(symp->nodep(), Class) || VN_IS(symp->nodep(), Package)) return true; - const AstRefDType* refDTypep = nullptr; + + // Helper: check if a RefDType might resolve to a class later + const auto checkUnresolvedRef = [](const AstRefDType* refp) -> bool { + return refp && !refp->typeofp() && !refp->classOrPackageOpp(); + }; + + // Helper: allow types that can represent a class/package handle or an unresolved ref. + // BasicDType is allowed here only for null/object handles. + const auto isValidTypeNode = [](const AstNode* nodep) -> bool { + return VN_IS(nodep, VoidDType) || VN_IS(nodep, BasicDType) + || VN_IS(nodep, ClassRefDType) || VN_IS(nodep, ParseRef); + }; + if (const AstTypedef* const typedefp = VN_CAST(symp->nodep(), Typedef)) { if (VN_IS(typedefp->childDTypep(), ClassRefDType)) return true; - if (const AstRefDType* const refp = VN_CAST(typedefp->childDTypep(), RefDType)) { - refDTypep = refp; - } + if (checkUnresolvedRef(VN_CAST(typedefp->childDTypep(), RefDType))) return true; } else if (const AstParamTypeDType* const paramTypep = VN_CAST(symp->nodep(), ParamTypeDType)) { - if (const AstRequireDType* const requireDTypep - = VN_CAST(paramTypep->childDTypep(), RequireDType)) { - if (const AstRefDType* const refp = VN_CAST(requireDTypep->lhsp(), RefDType)) { - refDTypep = refp; - } else if (VN_IS(requireDTypep->lhsp(), VoidDType) - || VN_IS(requireDTypep->lhsp(), BasicDType) - || VN_IS(requireDTypep->lhsp(), ClassRefDType)) { - return true; - } + // ParamTypeDType child may be wrapped in RequireDType or unwrapped + AstNode* childp = paramTypep->childDTypep(); + if (const AstRequireDType* const reqp = VN_CAST(childp, RequireDType)) { + childp = reqp->lhsp(); } - } - // TODO: this should be handled properly - case when it is known what type is - // referenced by AstRefDType (refDTypep->typeofp() is null or - // refDTypep->classOrPackageOpp() is null) - if (refDTypep && !refDTypep->typeofp() && !refDTypep->classOrPackageOpp()) { - // When still unknown - return because it may be a class, classes may not be - // linked at this point. Return in case it gets resolved to a class in the future - return true; + if (isValidTypeNode(childp)) return true; + if (checkUnresolvedRef(VN_CAST(childp, RefDType))) return true; } return false; } @@ -2730,14 +2754,27 @@ class LinkDotResolveVisitor final : public VNVisitor { return superNewStmtp; } void checkNoDot(AstNode* nodep) { + if (VL_UNLIKELY(!nodep)) { + UINFO(9, indent() << "iface capture null node passed to checkNoDot; dot state=" + << m_ds.ascii()); + m_ds.m_dotErr = true; + return; + } if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) { UINFO(9, indent() << "ds=" << m_ds.ascii()); - nodep->v3error("Syntax error: Not expecting " - << nodep->type() << " under a " << nodep->backp()->type() - << " in dotted expression\n" - << nodep->warnContextPrimary() << m_ds.m_dotp->warnOther() - << "... Resolving this reference\n" - << m_ds.m_dotp->warnContextSecondary()); + UINFO(9, indent() << "iface capture checkNoDot hit node=" << nodep + << " dotState=" << m_ds.ascii()); + if (VL_UNLIKELY(!m_ds.m_dotp)) { + nodep->v3error("Syntax error: Not expecting " << nodep->type() + << " here (missing dot context)"); + } else { + nodep->v3error("Syntax error: Not expecting " + << nodep->type() << " under a " << nodep->backp()->type() + << " in dotted expression\n" + << nodep->warnContextPrimary() << m_ds.m_dotp->warnOther() + << "... Resolving this reference\n" + << m_ds.m_dotp->warnContextSecondary()); + } m_ds.m_dotErr = true; } } @@ -2774,6 +2811,54 @@ class LinkDotResolveVisitor final : public VNVisitor { << origp->warnContextSecondary()); } } + // This helper clones the RefDType (including the user2 context), wraps it in a ParamTypeDType + // with the original var name, and returns the new dtype so the caller can ultimately replace + // the var and continue typedef retargeting. (used in cases like: localparam rq_t = + // bus_io.rq_t;) + AstParamTypeDType* promoteVarToParamType(AstVar* varp, AstRefDType* typedefRefp) { + if (!varp || !typedefRefp) return nullptr; + VSymEnt* const varSymp = varp->user1u().toSymEnt(); + if (!varSymp) return nullptr; + VSymEnt* const parentSymp = varSymp->parentp(); + if (!parentSymp) return nullptr; + UINFO(9, indent() << "iface capture promote var to ParamType name=" << varp->prettyName() + << " dotState(before)=" << m_ds.ascii()); + AstParamTypeDType* const newTypep = new AstParamTypeDType{ + varp->fileline(), varp->varType(), VFwdType::NONE, + varp->name(), VFlagChildDType{}, typedefRefp->cloneTree(false)}; + if (AstRefDType* const clonedRefp = VN_CAST(newTypep->childDTypep(), RefDType)) { + clonedRefp->user2p(typedefRefp->user2p()); + if (V3LinkDotIfaceCapture::enabled()) { + if (V3LinkDotIfaceCapture::replaceRef(typedefRefp, clonedRefp)) { + UINFO(9, indent() << "iface capture retarget captured typedef var=" + << varp->prettyName() << " orig=" << typedefRefp + << " clone=" << clonedRefp); + } + if (typedefRefp->user2p()) { + UINFO(9, indent() << "iface capture capture recorded owner var=" + << varp->prettyName() << " typedef=" << clonedRefp + << " cell=" << clonedRefp->user2p()); + } + } + } + VSymEnt* const newSymEntp = new VSymEnt{m_statep->symsp(), newTypep}; + newSymEntp->parentp(parentSymp); + newSymEntp->fallbackp(varSymp->fallbackp()); + newSymEntp->classOrPackagep(varSymp->classOrPackagep()); + newSymEntp->exported(varSymp->exported()); + newSymEntp->imported(varSymp->imported()); + newTypep->user1p(newSymEntp); + parentSymp->reinsert(varp->name(), newSymEntp); + varp->replaceWith(newTypep); + // This conversion happens while linkDot is in the middle of a dotted lookup (e.g. + // bus_io.rq_t). Reset the dot state so subsequent symbols in this scope do not inherit the + // pending dot. + m_ds.init(m_curSymp); + UINFO(9, indent() << "iface capture converted owner var to ParamType name=" + << varp->prettyName() << " dotState(after-reset)=" << m_ds.ascii()); + VL_DO_DANGLING(pushDeletep(varp), varp); + return newTypep; + } VSymEnt* getCreateClockingEventSymEnt(AstClocking* clockingp) { AstVar* const eventp = clockingp->ensureEventp(true); if (!eventp->user1p()) eventp->user1p(new VSymEnt{m_statep->symsp(), eventp}); @@ -3121,6 +3206,8 @@ class LinkDotResolveVisitor final : public VNVisitor { iterateChildren(nodep); m_modp = nullptr; m_ds.m_dotSymp = m_curSymp = m_modSymp = nullptr; + m_ds.m_dotPos = DP_NONE; + m_ds.m_dotErr = false; } void visit(AstScope* nodep) override { LINKDOT_VISIT_START(); @@ -3894,13 +3981,32 @@ class LinkDotResolveVisitor final : public VNVisitor { } } } else if (AstTypedef* const defp = VN_CAST(foundp->nodep(), Typedef)) { - ok = m_ds.m_dotPos == DP_NONE || m_ds.m_dotPos == DP_SCOPE; + const bool ifaceFinalSegmentAllowed + = (m_ds.m_dotPos == DP_FINAL) && m_ds.m_dotSymp + && VN_IS(m_ds.m_dotSymp->nodep(), Cell) + && VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp() + && VN_IS(VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp(), Iface); + ok = (m_ds.m_dotPos == DP_NONE || m_ds.m_dotPos == DP_SCOPE + || (V3LinkDotIfaceCapture::enabled() && ifaceFinalSegmentAllowed)); + if (V3LinkDotIfaceCapture::enabled() && ifaceFinalSegmentAllowed) { + UINFO(9, indent() << "iface capture allow final-segment typedef name=" + << nodep->name() << " dotText='" << m_ds.m_dotText + << "' dotSym=" << m_ds.m_dotSymp); + } if (ok) { AstRefDType* const refp = new AstRefDType{nodep->fileline(), nodep->name()}; // Don't check if typedef is to a :: as might not be // resolved yet if (m_ds.m_dotPos == DP_NONE) checkDeclOrder(nodep, defp); refp->typedefp(defp); + + V3LinkDotIfaceCapture::captureTypedefContext( + refp, "typedef", static_cast(m_ds.m_dotPos), + m_ds.m_dotPos == DP_FINAL, m_ds.m_dotText, m_ds.m_dotSymp, m_curSymp, + m_modp, nodep, + [this](AstVar* v, AstRefDType* r) { return promoteVarToParamType(v, r); }, + [this]() { return indent(); }); + if (VN_IS(nodep->backp(), SelExtract)) { m_packedArrayDtp = refp; } else { @@ -3913,6 +4019,14 @@ class LinkDotResolveVisitor final : public VNVisitor { if (ok) { AstRefDType* const refp = new AstRefDType{nodep->fileline(), nodep->name()}; refp->refDTypep(defp); + + V3LinkDotIfaceCapture::captureTypedefContext( + refp, "paramtype", static_cast(m_ds.m_dotPos), + m_ds.m_dotPos == DP_FINAL, m_ds.m_dotText, m_ds.m_dotSymp, m_curSymp, + m_modp, nodep, + [this](AstVar* v, AstRefDType* r) { return promoteVarToParamType(v, r); }, + [this]() { return indent(); }); + replaceWithCheckBreak(nodep, refp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } @@ -4987,8 +5101,52 @@ class LinkDotResolveVisitor final : public VNVisitor { m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp); } void visit(AstRefDType* nodep) override { + + if (auto* const typeOfp = nodep->typeofp()) { + iterate(typeOfp); + // After iteration, typeofp() may have been replaced with a resolved type + // (e.g., DOT expression like if0.rq_t resolved to REFDTYPE) + // If it's now a NodeDType, replace this RefDType with it + if (AstNodeDType* const resolvedDTypep = VN_CAST(nodep->typeofp(), NodeDType)) { + UINFO(9, + indent() << "iface capture typeofp resolved to dtype, replacing RefDType\n"); + resolvedDTypep->unlinkFrBack(); + nodep->replaceWith(resolvedDTypep); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + // If the resolved dtype is a RefDType with an interface typedef, + // ensure it's captured for re-resolution during paramed pass + if (AstRefDType* const resolvedRefp = VN_CAST(resolvedDTypep, RefDType)) { + if (resolvedRefp->user2p() && !V3LinkDotIfaceCapture::find(resolvedRefp)) { + AstCell* const cellp = VN_AS(resolvedRefp->user2p(), Cell); + UINFO(9, indent() << "iface capture re-capture resolved RefDType=" + << resolvedRefp << " cell=" << cellp << "\n"); + V3LinkDotIfaceCapture::add(resolvedRefp, cellp, m_modp, + resolvedRefp->typedefp()); + } + } + } + return; + } + // Resolve its reference - if (nodep->user3SetOnce()) return; + if (V3LinkDotIfaceCapture::find(nodep)) { + UINFO(9, indent() << "iface capture visit captured typedef ptr=" << nodep + << " user2=" << nodep->user2p()); + } + if (m_statep->forParamed() && nodep->user3()) { + if (V3LinkDotIfaceCapture::enabled() && nodep->user2p()) { + UINFO(9, indent() << "iface capture clear user3 for captured typedef name=" + << nodep->name() << " cell=" << nodep->user2p()); + } + nodep->user3(false); + } + if (nodep->user3SetOnce()) { + if (V3LinkDotIfaceCapture::enabled() && nodep->user2p()) { + UINFO(9, indent() << "iface capture skip revisit name=" << nodep->name() + << " already user3 and captured cell=" << nodep->user2p()); + } + return; + } LINKDOT_VISIT_START(); UINFO(5, indent() << "visit " << nodep); if (AstNode* const cpackagep = nodep->classOrPackageOpp()) { @@ -5030,19 +5188,80 @@ class LinkDotResolveVisitor final : public VNVisitor { } VL_DO_DANGLING(pushDeletep(cpackagep->unlinkFrBack()), cpackagep); } + + const bool capEnable = V3LinkDotIfaceCapture::enabled(); + const auto* const capEntryp = capEnable ? V3LinkDotIfaceCapture::find(nodep) : nullptr; + const bool captureMapHit = capEntryp != nullptr; + AstCell* const captureEntryCellp = capEntryp ? capEntryp->cellp : nullptr; + AstTypedef* const capturedTypedefp = capEntryp ? capEntryp->typedefp : nullptr; + const VSymEnt* const capturedTypedefSymp + = capturedTypedefp ? m_statep->getNodeSym(capturedTypedefp) : nullptr; + + const bool ifaceCaptured = capEnable && nodep->user2p(); + const bool missingIfaceContext = captureMapHit && !ifaceCaptured; + const char* const passLabel = m_statep->forParamed() ? "paramed" : "primary"; + if (missingIfaceContext) { + UINFO(9, indent() << "iface capture captured typedef missing user2 name=" + << nodep->name() << " ref=" << nodep << " pass=" << passLabel + << " entryCell=" << captureEntryCellp); + } + AstCell* const capturedCellp = ifaceCaptured ? VN_CAST(nodep->user2p(), Cell) : nullptr; + + bool forcedIfaceDotScope = false; + bool resolvedCapturedTypedef = false; + bool captureEntryRetired = false; + const auto retireCapture = [&](const char* reason) { + if (!ifaceCaptured || captureEntryRetired) return; + const auto* entry = V3LinkDotIfaceCapture::find(nodep); + AstCell* const entryCell = entry ? entry->cellp : nullptr; + UINFO(9, indent() << "iface capture retire captured typedef reason=" << reason + << " name=" << nodep->name() << " pass=" << passLabel + << " user2=" << nodep->user2p() << " entryCell=" << entryCell); + const bool erased = V3LinkDotIfaceCapture::erase(nodep); + captureEntryRetired = true; + UINFO(9, indent() << "iface capture retire erase result name=" << nodep->name() + << " erased=" << erased); + }; + if (ifaceCaptured && m_statep->forParamed()) { + UINFO(9, indent() << "iface capture captured typedef name=" << nodep->name() + << " typedef=" << nodep->typedefp() << " cell=" << capturedCellp); + if (nodep->typedefp()) { + UINFO(9, indent() << "iface capture refresh typedef binding name=" << nodep->name() + << " typedef=" << nodep->typedefp() + << " cell=" << capturedCellp); + nodep->typedefp(nullptr); + nodep->classOrPackagep(nullptr); + } + } if (m_ds.m_dotp && (m_ds.m_dotPos == DP_PACKAGE || m_ds.m_dotPos == DP_SCOPE)) { UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), ClassOrPackageRef), m_ds.m_dotp->lhsp(), "Bad package link"); - auto* const cpackagerefp = VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef); + auto* const cpackagerefp = VN_CAST(m_ds.m_dotp->lhsp(), ClassOrPackageRef); UASSERT_OBJ(cpackagerefp->classOrPackageSkipp(), m_ds.m_dotp->lhsp(), "Bad package link"); nodep->classOrPackagep(cpackagerefp->classOrPackageSkipp()); m_ds.m_dotPos = DP_SCOPE; - } else { + } else if (!ifaceCaptured) { checkNoDot(nodep); + } else { + UINFO(9, indent() << "iface capture consume captured iface context name=" + << nodep->name() << " cell=" << capturedCellp); + m_ds.m_dotPos = DP_SCOPE; + forcedIfaceDotScope = true; + // Set dotSymp to the cell's symbol entry so lookup happens in the interface scope + if (capturedCellp && m_statep->existsNodeSym(capturedCellp)) { + VSymEnt* const cellSymp = m_statep->getNodeSym(capturedCellp); + m_ds.m_dotSymp = cellSymp; + UINFO(9, indent() << "iface capture set dotSymp to cell scope cellSymp=" + << cellSymp << " node=" << cellSymp->nodep()); + } } - if (nodep->typeofp()) { // Really is a typeof not a reference - } else if (!nodep->typedefp() && !nodep->subDTypep()) { + if (!nodep->typedefp() && !nodep->subDTypep()) { + if (ifaceCaptured) { + UINFO(9, indent() << "iface capture lookup start name=" << nodep->name() + << " dotPos=" << static_cast(m_ds.m_dotPos) << " dotSym=" + << m_ds.m_dotSymp << " classPkg=" << nodep->classOrPackagep()); + } const VSymEnt* foundp; if (nodep->classOrPackagep()) { foundp = m_statep->getNodeSym(nodep->classOrPackagep())->findIdFlat(nodep->name()); @@ -5052,52 +5271,105 @@ class LinkDotResolveVisitor final : public VNVisitor { } else if (m_ds.m_dotPos == DP_FIRST || m_ds.m_dotPos == DP_NONE) { foundp = m_curSymp->findIdFallback(nodep->name()); } else { - foundp = m_curSymp->findIdFlat(nodep->name()); + //foundp = m_curSymp->findIdFlat(nodep->name()); + // Use dotSymp if set (e.g., for captured interface typedefs), else curSymp + VSymEnt* const lookupSymp = m_ds.m_dotSymp ? m_ds.m_dotSymp : m_curSymp; + foundp = lookupSymp->findIdFlat(nodep->name()); } - if (AstTypedef* const defp = foundp ? VN_CAST(foundp->nodep(), Typedef) : nullptr) { - // Don't check if typedef is to a :: as might not be resolved - // yet - if (!nodep->classOrPackagep() && !defp->isUnderClass()) - checkDeclOrder(nodep, defp); - nodep->typedefp(defp); - nodep->classOrPackagep(foundp->classOrPackagep()); - } else if (AstParamTypeDType* const defp - = foundp ? VN_CAST(foundp->nodep(), ParamTypeDType) : nullptr) { - if (defp == nodep->backp()) { // Where backp is typically typedef - nodep->v3error("Reference to '" << m_ds.m_dotText - << (m_ds.m_dotText == "" ? "" : ".") - << nodep->prettyName() << "'" - << " type would form a recursive definition"); - nodep->refDTypep(nodep->findVoidDType()); // Try to reduce later errors - } else { - nodep->refDTypep(defp); + if (!foundp && ifaceCaptured && capturedTypedefp) { + UINFO(9, indent() << "iface capture binding via captured typedef fallback name=" + << nodep->name() << " typedef=" << capturedTypedefp); + nodep->typedefp(capturedTypedefp); + nodep->classOrPackagep(capturedTypedefSymp ? capturedTypedefSymp->classOrPackagep() + : nullptr); + resolvedCapturedTypedef = true; + } + if (!resolvedCapturedTypedef && foundp) { + VSymEnt* const parentSymp = foundp->parentp(); + UINFO(9, indent() << "iface capture resolved typedef name=" << nodep->name() + << " foundNode=" << foundp->nodep() << " parentNode=" + << (parentSymp ? parentSymp->nodep() : nullptr)); + } + if (!resolvedCapturedTypedef) { + if (AstTypedef* const defp + = foundp ? VN_CAST(foundp->nodep(), Typedef) : nullptr) { + // Don't check if typedef is to a :: as might not be + // resolved yet + if (!nodep->classOrPackagep() && !defp->isUnderClass()) + checkDeclOrder(nodep, defp); + nodep->typedefp(defp); nodep->classOrPackagep(foundp->classOrPackagep()); - } - } else if (AstClass* const defp = foundp ? VN_CAST(foundp->nodep(), Class) : nullptr) { - // Don't check if typedef is to a :: as might not be resolved - // yet - if (!nodep->classOrPackagep()) checkDeclOrder(nodep, defp); - AstPin* const paramsp = nodep->paramsp(); - if (paramsp) paramsp->unlinkFrBackWithNext(); - AstClassRefDType* const newp - = new AstClassRefDType{nodep->fileline(), defp, paramsp}; - newp->classOrPackagep(foundp->classOrPackagep()); - nodep->replaceWith(newp); - VL_DO_DANGLING(pushDeletep(nodep), nodep); - return; - } else if (m_insideClassExtParam) { - return; - } else { - if (foundp) { - UINFO(1, "Found sym node: " << foundp->nodep()); - nodep->v3error("Expecting a data type: " << nodep->prettyNameQ()); + resolvedCapturedTypedef = true; + } else if (AstParamTypeDType* const defp + = foundp ? VN_CAST(foundp->nodep(), ParamTypeDType) : nullptr) { + if (defp == nodep->backp()) { // Where backp is typically typedef + nodep->v3error("Reference to '" + << m_ds.m_dotText << (m_ds.m_dotText == "" ? "" : ".") + << nodep->prettyName() << "'" + << " type would form a recursive definition"); + nodep->refDTypep(nodep->findVoidDType()); // Try to reduce later errors + } else { + nodep->refDTypep(defp); + nodep->classOrPackagep(foundp->classOrPackagep()); + resolvedCapturedTypedef = true; + } + } else if (AstClass* const defp + = foundp ? VN_CAST(foundp->nodep(), Class) : nullptr) { + // Don't check if typedef is to a :: as might not be + // resolved yet + if (!nodep->classOrPackagep()) checkDeclOrder(nodep, defp); + AstPin* const paramsp = nodep->paramsp(); + if (paramsp) paramsp->unlinkFrBackWithNext(); + AstClassRefDType* const newp + = new AstClassRefDType{nodep->fileline(), defp, paramsp}; + newp->classOrPackagep(foundp->classOrPackagep()); + resolvedCapturedTypedef = true; + retireCapture("resolved"); // Must retire before replacing node + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } else if (m_insideClassExtParam) { + return; } else { - nodep->v3error("Can't find typedef/interface: " << nodep->prettyNameQ()); + if (foundp) { + UINFO(1, "Found sym node: " << foundp->nodep()); + nodep->v3error("Expecting a data type: " << nodep->prettyNameQ()); + } else { + nodep->v3error("Can't find typedef/interface: " << nodep->prettyNameQ()); + } } } } + if (forcedIfaceDotScope && m_ds.m_dotPos == DP_SCOPE && !m_ds.m_dotp) { + UINFO(9, indent() << "iface capture reset dot state after captured typedef name=" + << nodep->name()); + m_ds.init(m_curSymp); + } + if (ifaceCaptured && resolvedCapturedTypedef) { retireCapture("resolved"); } iterateChildren(nodep); } + void visit(AstRequireDType* nodep) override { + // Handle type validation for localparam type assignments + // Iterate child to resolve any ParseRef nodes + iterateChildren(nodep); + // Only emit error if the child is not a type. + // Do NOT unwrap valid types here - leave that to V3Width. + // Unwrapping here breaks type parameter resolution during cloning. + if (nodep->lhsp() && !VN_IS(nodep->lhsp(), NodeDType)) { + // Not a type - emit error + if (AstConst* const constp = VN_CAST(nodep->lhsp(), Const)) { + nodep->lhsp()->v3error( + "Expecting a data type, not a constant: " << constp->toSInt()); + } else { + nodep->lhsp()->v3error("Expecting a data type, not " + << nodep->lhsp()->typeName() << ": '" + << nodep->lhsp()->prettyName() << "'"); + } + // Replace with void to keep AST valid and allow clean exit + nodep->replaceWith(new AstVoidDType{nodep->fileline()}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + } void visit(AstDpiExport* nodep) override { // AstDpiExport: Make sure the function referenced exists, then dump it LINKDOT_VISIT_START(); @@ -5234,6 +5506,14 @@ public: LinkDotResolveVisitor(AstNetlist* rootp, LinkDotState* statep) : m_statep{statep} { UINFO(4, __FUNCTION__ << ": "); + + if (m_statep->forParamed()) { + V3LinkDotIfaceCapture::forEach( + [](const V3LinkDotIfaceCapture::CapturedIfaceTypedef& entry) { + if (AstRefDType* const refp = entry.refp) refp->user3(false); + }); + } + iterate(rootp); std::map modulesToRevisit = std::move(m_modulesToRevisit); m_lastDeferredp = nullptr; diff --git a/src/V3LinkDotIfaceCapture.cpp b/src/V3LinkDotIfaceCapture.cpp new file mode 100644 index 000000000..b11e622d8 --- /dev/null +++ b/src/V3LinkDotIfaceCapture.cpp @@ -0,0 +1,218 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#include "V3LinkDotIfaceCapture.h" + +#include "V3Error.h" +#include "V3Global.h" + +VL_DEFINE_DEBUG_FUNCTIONS; + +V3LinkDotIfaceCapture::CapturedMap V3LinkDotIfaceCapture::s_map{}; + +bool V3LinkDotIfaceCapture::s_enabled = true; + +AstNodeModule* V3LinkDotIfaceCapture::findOwnerModule(AstNode* nodep) { + for (AstNode* curp = nodep; curp; curp = curp->backp()) { + if (AstNodeModule* const modp = VN_CAST(curp, NodeModule)) return modp; + } + return nullptr; +} + +bool V3LinkDotIfaceCapture::finalizeCapturedEntry(CapturedMap::iterator it, const char* reasonp) { + CapturedIfaceTypedef& entry = it->second; + AstRefDType* const pendingRefp = entry.pendingClonep; + AstTypedef* const reboundTypedefp = entry.typedefp; + if (!pendingRefp || !reboundTypedefp) return false; + if (entry.cellp) pendingRefp->user2p(entry.cellp); + pendingRefp->user3(false); + pendingRefp->typedefp(reboundTypedefp); + entry.pendingClonep = nullptr; + return true; +} + +string V3LinkDotIfaceCapture::extractIfacePortName(const string& dotText) { + string name = dotText; + const size_t dotPos = name.find('.'); + if (dotPos != string::npos) name = name.substr(0, dotPos); + const size_t braPos = name.find("__BRA__"); + if (braPos != string::npos) name = name.substr(0, braPos); + return name; +} + +void V3LinkDotIfaceCapture::add(AstRefDType* refp, AstCell* cellp, AstNodeModule* ownerModp, + AstTypedef* typedefp, AstNodeModule* typedefOwnerModp, + AstVar* ifacePortVarp) { + if (!refp) return; + + if (!typedefp) typedefp = refp->typedefp(); + + if (!typedefOwnerModp && typedefp) typedefOwnerModp = findOwnerModule(typedefp); + + s_map[refp] = CapturedIfaceTypedef{ + refp, cellp, ownerModp, typedefp, typedefOwnerModp, nullptr, ifacePortVarp}; +} + +const V3LinkDotIfaceCapture::CapturedIfaceTypedef* +V3LinkDotIfaceCapture::find(const AstRefDType* refp) { + if (!refp) return nullptr; + const auto it = s_map.find(refp); + if (VL_UNLIKELY(it == s_map.end())) return nullptr; + return &it->second; +} + +bool V3LinkDotIfaceCapture::erase(const AstRefDType* refp) { + if (!refp) return false; + const auto it = s_map.find(refp); + if (it == s_map.end()) return false; + s_map.erase(it); + return true; +} + +bool V3LinkDotIfaceCapture::replaceRef(const AstRefDType* oldRefp, AstRefDType* newRefp) { + if (!oldRefp || !newRefp) return false; + const auto it = s_map.find(oldRefp); + if (it == s_map.end()) return false; + auto entry = it->second; + entry.refp = newRefp; + s_map.erase(it); + s_map.emplace(newRefp, entry); + return true; +} + +bool V3LinkDotIfaceCapture::replaceTypedef(const AstRefDType* refp, AstTypedef* newTypedefp) { + if (!refp || !newTypedefp) return false; + auto it = s_map.find(refp); + if (it == s_map.end()) return false; + it->second.typedefp = newTypedefp; + it->second.typedefOwnerModp = findOwnerModule(newTypedefp); + finalizeCapturedEntry(it, "typedef clone"); + return true; +} + +void V3LinkDotIfaceCapture::propagateClone(const AstRefDType* origRefp, AstRefDType* newRefp) { + if (!origRefp || !newRefp) return; + auto it = s_map.find(origRefp); + UASSERT_OBJ(it != s_map.end(), origRefp, + "iface capture propagateClone missing entry for orig=" << cvtToStr(origRefp)); + CapturedIfaceTypedef& entry = it->second; + + if (entry.cellp) newRefp->user2p(entry.cellp); + newRefp->user3(false); + entry.pendingClonep = newRefp; + + // If replaceTypedef was already called (interface cloned before module), + // entry.typedefp will differ from the original RefDType's typedef. + // In that case, finalize now with the updated typedef. + if (entry.typedefp && origRefp->typedefp() && entry.typedefp != origRefp->typedefp()) { + finalizeCapturedEntry(it, "ref clone"); + } +} + +template +void V3LinkDotIfaceCapture::forEachImpl(FilterFn&& filter, Fn&& fn) { + std::vector keys; + keys.reserve(s_map.size()); + for (const auto& kv : s_map) keys.push_back(kv.first); + + for (const AstRefDType* key : keys) { + const auto it = s_map.find(key); + if (it == s_map.end()) continue; + + CapturedIfaceTypedef& entry = it->second; + if (entry.cellp && entry.refp && entry.refp->user2p() != entry.cellp) { + entry.refp->user2p(entry.cellp); + } + if (!filter(entry)) continue; + fn(entry); + } +} + +void V3LinkDotIfaceCapture::forEach(const std::function& fn) { + if (!fn) return; + forEachImpl([](const CapturedIfaceTypedef&) { return true; }, fn); +} + +void V3LinkDotIfaceCapture::forEachOwned( + const AstNodeModule* ownerModp, const std::function& fn) { + if (!ownerModp || !fn) return; + forEachImpl( + [ownerModp](const CapturedIfaceTypedef& e) { + return e.ownerModp == ownerModp || e.typedefOwnerModp == ownerModp; + }, + fn); +} + +// replaces the lambda used in V3LinkDot.cpp for iface capture +void V3LinkDotIfaceCapture::captureTypedefContext( + AstRefDType* refp, const char* stageLabel, int dotPos, bool dotIsFinal, + const std::string& dotText, VSymEnt* dotSymp, VSymEnt* curSymp, AstNodeModule* modp, + AstNode* nodep, const std::function& promoteVarCb, + const std::function& indentFn) { + if (!enabled() || !refp) return; + + UINFO(9, indentFn() << "iface capture capture request stage=" << stageLabel + << " typedef=" << refp << " name=" << refp->name() << " dotPos=" << dotPos + << " dotText='" << dotText << "' dotSym=" << dotSymp); + + const AstCell* ifaceCellp = nullptr; + if (dotSymp && VN_IS(dotSymp->nodep(), Cell)) { + const AstCell* const cellp = VN_AS(dotSymp->nodep(), Cell); + if (cellp->modp() && VN_IS(cellp->modp(), Iface)) ifaceCellp = cellp; + } + if (!ifaceCellp) { + UINFO(9, indentFn() << "iface capture capture skipped typedef=" << refp + << " (no iface context)"); + return; + } + + AstVar* ifacePortVarp = nullptr; + if (!dotText.empty() && curSymp) { + const std::string portName = extractIfacePortName(dotText); + if (VSymEnt* const portSymp = curSymp->findIdFallback(portName)) { + ifacePortVarp = VN_CAST(portSymp->nodep(), Var); + UINFO(9, indentFn() << "iface capture found port var '" << portName << "' -> " + << ifacePortVarp); + } + } + + refp->user2p(const_cast(ifaceCellp)); + V3LinkDotIfaceCapture::add(refp, const_cast(ifaceCellp), modp, refp->typedefp(), + nullptr, ifacePortVarp); + + UINFO(9, indentFn() << "iface capture capture success typedef=" << refp + << " cell=" << ifaceCellp + << " mod=" << (ifaceCellp->modp() ? ifaceCellp->modp()->name() : "") + << " dotPos=" << dotPos); + if (!dotIsFinal) return; + + AstVar* enclosingVarp = nullptr; + for (AstNode* curp = nodep; curp; curp = curp->backp()) { + if (AstVar* const varp = VN_CAST(curp, Var)) { + enclosingVarp = varp; + break; + } + if (VN_IS(curp, ParamTypeDType)) break; + if (VN_IS(curp, NodeModule)) break; + } + if (!enclosingVarp || enclosingVarp->user3SetOnce()) return; + UINFO(9, indentFn() << "iface capture typedef owner var=" << enclosingVarp + << " name=" << enclosingVarp->prettyName()); + + if (promoteVarCb && promoteVarCb(enclosingVarp, refp)) return; + UINFO(9, indentFn() << "iface capture failed to convert owner var name=" + << enclosingVarp->prettyName()); +} diff --git a/src/V3LinkDotIfaceCapture.h b/src/V3LinkDotIfaceCapture.h new file mode 100644 index 000000000..8a3517aa0 --- /dev/null +++ b/src/V3LinkDotIfaceCapture.h @@ -0,0 +1,90 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Interface typedef capture helper. +// Stores (refp, typedefp, cellp, owners, pendingClone) so LinkDot can +// rebind refs when symbol lookup fails, and V3Param clones can retarget +// typedefs without legacy paths. +// +// Code available from: https://verilator.org +// +//************************************************************************* +// +// Copyright 2003-2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifndef VERILATOR_V3LINKDOTIFACECAPTURE_H_ +#define VERILATOR_V3LINKDOTIFACECAPTURE_H_ + +#include "config_build.h" + +#include "V3Ast.h" +#include "V3SymTable.h" + +#include +#include +#include + +class V3LinkDotIfaceCapture final { +public: + struct CapturedIfaceTypedef final { + AstRefDType* refp = nullptr; + AstCell* cellp = nullptr; + // Module where the RefDType lives + AstNodeModule* ownerModp = nullptr; + // Typedef definition being referenced + AstTypedef* typedefp = nullptr; + // Interface/module that owns typedefp + AstNodeModule* typedefOwnerModp = nullptr; + // Cloned RefDType awaiting typedef rebinding + AstRefDType* pendingClonep = nullptr; + // Interface port variable for matching during cloning + AstVar* ifacePortVarp = nullptr; + }; + + using CapturedMap = std::unordered_map; + +private: + static CapturedMap s_map; + static bool s_enabled; + + static AstNodeModule* findOwnerModule(AstNode* nodep); + static bool finalizeCapturedEntry(CapturedMap::iterator it, const char* reasonp); + static string extractIfacePortName(const string& dotText); + + template + static void forEachImpl(FilterFn&& filter, Fn&& fn); + +public: + static void enable(bool flag) { + s_enabled = flag; + if (!flag) s_map.clear(); + } + static bool enabled() { return s_enabled; } + static void reset() { s_map.clear(); } + static void add(AstRefDType* refp, AstCell* cellp, AstNodeModule* ownerModp, + AstTypedef* typedefp = nullptr, AstNodeModule* typedefOwnerModp = nullptr, + AstVar* ifacePortVarp = nullptr); + static const CapturedIfaceTypedef* find(const AstRefDType* refp); + static void forEach(const std::function& fn); + static void forEachOwned(const AstNodeModule* ownerModp, + const std::function& fn); + static bool replaceRef(const AstRefDType* oldRefp, AstRefDType* newRefp); + static bool replaceTypedef(const AstRefDType* refp, AstTypedef* newTypedefp); + static bool erase(const AstRefDType* refp); + static std::size_t size() { return s_map.size(); } + static void propagateClone(const AstRefDType* origRefp, AstRefDType* newRefp); + + static void + captureTypedefContext(AstRefDType* refp, const char* stageLabel, int dotPos, bool dotIsFinal, + const std::string& dotText, VSymEnt* dotSymp, VSymEnt* curSymp, + AstNodeModule* modp, AstNode* nodep, + const std::function& promoteVarCb, + const std::function& indentFn); +}; + +#endif // VERILATOR_V3LINKDOTIFACECAPTURE_H_ diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 5ab18cfc9..2c82c2af0 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -53,6 +53,7 @@ #include "V3Const.h" #include "V3EmitV.h" #include "V3Hasher.h" +#include "V3LinkDotIfaceCapture.h" #include "V3Os.h" #include "V3Parse.h" #include "V3Simulate.h" @@ -61,6 +62,7 @@ #include "V3Width.h" #include +#include #include #include #include @@ -623,6 +625,75 @@ class ParamProcessor final { newModp = srcModp->cloneTree(false); } + // cloneTree(false) temporarily populates origNode->clonep() for every node under + // srcModp. The capture list still stores those orig AstRefDType* pointers, so walking + // it lets us follow clonep() into newModp and scrub each clone with the saved + // interface context before newModp is re-linked. we have pointers to the same nodes saved + // in the capture map, so we can use them to scrub the new module. + if (V3LinkDotIfaceCapture::enabled()) { + V3LinkDotIfaceCapture::forEachOwned( + srcModp, [&](const V3LinkDotIfaceCapture::CapturedIfaceTypedef& entry) { + if (!entry.refp) return; + AstTypedef* const origTypedefp = entry.typedefp; + if (!origTypedefp) return; + + // Find the correct typedef from the correct interface clone. + // entry.typedefp points to the original interface's typedef, + // but we need the typedef in the interface clone this module connects to. + AstTypedef* targetTypedefp = nullptr; + const string& typedefName = origTypedefp->name(); + + for (auto it = ifaceRefRefs.cbegin(); it != ifaceRefRefs.cend(); ++it) { + const AstIfaceRefDType* const portIrefp = it->first; + AstNodeModule* const pinIfacep = it->second->ifaceViaCellp(); + if (!pinIfacep) continue; + + // If we have a port variable, match against it + if (entry.ifacePortVarp) { + // Get the IfaceRefDType from the captured port variable + AstNodeDType* const portDTypep = entry.ifacePortVarp->subDTypep(); + AstIfaceRefDType* entryPortIrefp = VN_CAST(portDTypep, IfaceRefDType); + if (!entryPortIrefp && arraySubDTypep(portDTypep)) { + entryPortIrefp + = VN_CAST(arraySubDTypep(portDTypep), IfaceRefDType); + } + if (entryPortIrefp != portIrefp) continue; // Not the right port + } + + // Search for typedef with same name in the connected interface clone + for (AstNode* stmtp = pinIfacep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + if (AstTypedef* const tdp = VN_CAST(stmtp, Typedef)) { + if (tdp->name() == typedefName) { + targetTypedefp = tdp; + UINFO(8, + " [iface-capture] found '" + << typedefName << "' in " << pinIfacep->name() + << " via port " + << (entry.ifacePortVarp ? entry.ifacePortVarp->name() + : "") + << endl); + break; + } + } + } + if (targetTypedefp) break; + } + + // Fallback to clone of original typedef (existing behavior) + if (!targetTypedefp) targetTypedefp = origTypedefp->clonep(); + + if (targetTypedefp) { + UINFO(8, " [iface-capture] replaceTypedef " + << origTypedefp->name() << " -> " << targetTypedefp << endl); + V3LinkDotIfaceCapture::replaceTypedef(entry.refp, targetTypedefp); + } + // Propagate to cloned RefDType in new module + if (AstRefDType* const clonedRefp = entry.refp->clonep()) { + V3LinkDotIfaceCapture::propagateClone(entry.refp, clonedRefp); + } + }); + } + newModp->name(newname); newModp->user2(false); // We need to re-recurse this module once changed newModp->recursive(false); @@ -973,7 +1044,7 @@ class ParamProcessor final { // Only consider formal class type parameters (generic parameters), // not localparam type declarations inside the class body. if (!paramTypep->isGParam()) continue; - m_paramIndex.emplace(paramTypep, m_classParams.size()); + m_paramIndex.emplace(paramTypep, static_cast(m_classParams.size())); m_classParams.emplace_back(paramTypep, -1); } } @@ -1113,6 +1184,13 @@ class ParamProcessor final { UINFO(8, " Done with " << modInfop->m_modp); newModp = modInfop->m_modp; } + + const bool cloned = (newModp != srcModp); + UINFO(9, "iface capture module clone src=" << srcModp << " new=" << newModp << " name=" + << newModp->name() << " from cell=" << nodep + << " cellName=" << nodep->name() + << " cloned=" << cloned); + if (defaultsResolved) srcModp->user4p(newModp); for (auto* stmtp = newModp->stmtsp(); stmtp; stmtp = stmtp->nextp()) { diff --git a/src/verilog.y b/src/verilog.y index 945c90883..7466e0c13 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3104,11 +3104,15 @@ list_of_param_assignments: // ==IEEE: list_of_param_assignments ; type_assignment: // ==IEEE: type_assignment - // // note exptOrDataType being a data_type is only for yPARAMETER yTYPE + // // note exprOrDataType being a data_type is only for yPARAMETER yTYPE + // // Using exprOrDataType allows hierarchical refs like if0.rq_t + // // which get resolved to types during linking idAny/*new-parameter*/ sigAttrListE { $$ = VARDONEA($1, *$1, nullptr, $2); } - | idAny/*new-parameter*/ sigAttrListE '=' data_typeAny - { $$ = VARDONEA($1, *$1, nullptr, $2); $$->valuep($4); } + | idAny/*new-parameter*/ sigAttrListE '=' exprOrDataType + { $$ = VARDONEA($1, *$1, nullptr, $2); + // V3LinkParse will wrap this in RequireDType when creating ParamTypeDType + $$->valuep($4); } ; list_of_type_assignments: // ==IEEE: list_of_type_assignments diff --git a/test_regress/t/t_lparam_assign_iface_array_typedef.py b/test_regress/t/t_lparam_assign_iface_array_typedef.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_array_typedef.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_array_typedef.v b/test_regress/t/t_lparam_assign_iface_array_typedef.v new file mode 100644 index 000000000..fd6655de2 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_array_typedef.v @@ -0,0 +1,69 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// + +`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); + +package a_pkg; + typedef struct packed { + int unsigned IdBits; + } cfg_t; +endpackage + +interface bus_if #( + parameter a_pkg::cfg_t cfg = 0 +)(); + typedef logic [cfg.IdBits-1:0] id_t; + id_t id; +endinterface + +module a_mod #()( + bus_if bus_tgt_io + ,bus_if bus_mst_io +); + + localparam type tgt_id_t = bus_tgt_io.id_t; + localparam type mst_id_t = bus_mst_io.id_t; + + tgt_id_t tgt_id; + mst_id_t mst_id; + + initial begin + #10; + `checkd($bits(tgt_id), 5); + `checkd($bits(mst_id), 10); + end + +endmodule + +module t( + input logic clk +); + localparam a_pkg::cfg_t cfg0 = '{IdBits: 5}; + localparam a_pkg::cfg_t cfg1 = '{IdBits: 10}; + + bus_if #(.cfg(cfg0)) bus_io0(); + bus_if #(.cfg(cfg1)) bus_io1(); + + a_mod a_mod0( + .bus_tgt_io(bus_io0) + ,.bus_mst_io(bus_io1) + ); + + initial begin + #10; + #10; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_array_typedef2.py b/test_regress/t/t_lparam_assign_iface_array_typedef2.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_array_typedef2.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_array_typedef2.v b/test_regress/t/t_lparam_assign_iface_array_typedef2.v new file mode 100644 index 000000000..d077e3296 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_array_typedef2.v @@ -0,0 +1,69 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// + +`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); + +package a_pkg; + typedef struct packed { + int unsigned IdBits; + } cfg_t; +endpackage + +interface bus_if #( + parameter a_pkg::cfg_t cfg = 0 +)(); + typedef logic [cfg.IdBits-1:0] id_t; + id_t id; +endinterface + +module a_mod #()( + bus_if bus_tgt_io_a [2] + ,bus_if bus_mst_io_a [2] +); + + localparam type tgt_id_t = bus_tgt_io_a[0].id_t; + localparam type mst_id_t = bus_mst_io_a[0].id_t; + + tgt_id_t tgt_id; + mst_id_t mst_id; + + initial begin + #10; + `checkd($bits(tgt_id), 5); + `checkd($bits(mst_id), 10); + end + +endmodule + +module t( + input logic clk +); + localparam a_pkg::cfg_t cfg0 = '{IdBits: 5}; + localparam a_pkg::cfg_t cfg1 = '{IdBits: 10}; + + bus_if #(.cfg(cfg0)) bus_tgt_io_a [2] (); + bus_if #(.cfg(cfg1)) bus_mst_io_a [2] (); + + a_mod a_mod0( + .bus_tgt_io_a(bus_tgt_io_a) + ,.bus_mst_io_a(bus_mst_io_a) + ); + + initial begin + #10; + #10; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef.py b/test_regress/t/t_lparam_assign_iface_typedef.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef.v b/test_regress/t/t_lparam_assign_iface_typedef.v new file mode 100644 index 000000000..71fdbdb08 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef.v @@ -0,0 +1,59 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// assign localparam from interface typedef, single level nesting +// + +`define stop $stop +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +interface x_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + logic [p_dwidth-1:0] data; + } rq_t; + + typedef struct packed { + logic [p_dwidth-1:0] data; + } rs_t; +endinterface + +module top(); + x_if #( + .p_awidth(16) + ,.p_dwidth(8) + ) if0(); + + localparam p0_rq_t = if0.rq_t; + localparam p0_rs_t = if0.rs_t; + + p0_rq_t rq; + p0_rs_t rs; + + always_comb begin + rq.addr = 'h1234; + rq.data = 'h37; + rs.data = 'h5a; + end + + initial begin + #1; + `checkh(rq.addr, 16'h1234); + `checkh(rq.data, 8'h37); + `checkh(rs.data, 8'h5a); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef2.py b/test_regress/t/t_lparam_assign_iface_typedef2.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef2.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef2.v b/test_regress/t/t_lparam_assign_iface_typedef2.v new file mode 100644 index 000000000..2bcf96896 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef2.v @@ -0,0 +1,29 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// + +interface x_if #( + parameter int a_width = 3 +)(); + + typedef struct packed { + logic [a_width-1:0] addr; + } rq_t; +endinterface + +module top(); + x_if #( + .a_width(8) + ) if0(); + + localparam type p0_t = if0.rq_t; + + initial begin + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef3.py b/test_regress/t/t_lparam_assign_iface_typedef3.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef3.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef3.v b/test_regress/t/t_lparam_assign_iface_typedef3.v new file mode 100644 index 000000000..972755ba7 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef3.v @@ -0,0 +1,60 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// assign localparam from interface typedef, single level nesting +// + +`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); + +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +interface x_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +)(); + localparam int Bits = p_awidth + p_dwidth; + typedef struct packed { + logic [p_awidth-1:0] addr; + logic [p_dwidth-1:0] data; + } rq_t; +endinterface + +module top(); + x_if #( + .p_awidth(16) + ,.p_dwidth(8) + ) if0 [2](); + + localparam type p0_rq_t = if0[0].rq_t; + + p0_rq_t rq; + + always_comb begin + rq.addr = 'h1234; + rq.data = 'h37; + end + + initial begin + #1; + `checkd(if0[0].Bits,24); + `checkd($bits(rq),24); + `checkh(rq.addr,16'h1234); + `checkh(rq.data,8'h37); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested.py b/test_regress/t/t_lparam_assign_iface_typedef_nested.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested.v b/test_regress/t/t_lparam_assign_iface_typedef_nested.v new file mode 100644 index 000000000..a3cfb9665 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested.v @@ -0,0 +1,47 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// assign localparam from nested interface typedef +// + +interface y_if #( + parameter int p_awidth = 3 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq2_t; +endinterface + +interface x_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + logic [p_dwidth-1:0] data; + } rq_t; + + typedef struct packed { + logic [p_dwidth-1:0] data; + } rs_t; + + y_if#(.p_awidth(p_awidth)) y_if0(); +endinterface + +module top(); + x_if #( + .p_awidth(16) + ,.p_dwidth(8) + ) if0(); + + localparam p0_rq2_t = if0.y_if0.rq2_t; + + initial begin + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested2.py b/test_regress/t/t_lparam_assign_iface_typedef_nested2.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested2.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested2.v b/test_regress/t/t_lparam_assign_iface_typedef_nested2.v new file mode 100644 index 000000000..dccc8b428 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested2.v @@ -0,0 +1,61 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// assign localparam from nested interface typedef +// instance of type, assign to instance and check +// + +`define stop $stop +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +interface y_if #( + parameter int p_awidth = 3 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq2_t; +endinterface + +interface x_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + logic [p_dwidth-1:0] data; + } rq_t; + + typedef struct packed { + logic [p_dwidth-1:0] data; + } rs_t; + + y_if#(.p_awidth(p_awidth)) y_if0(); +endinterface + +module top(); + x_if #( + .p_awidth(16) + ,.p_dwidth(8) + ) if0(); + + localparam p0_rq2_t = if0.y_if0.rq2_t; + + p0_rq2_t p0_rq2; + + assign p0_rq2.addr = 16'hcafe; + + initial begin + #1; + `checkh(p0_rq2.addr, 16'hcafe); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested3.py b/test_regress/t/t_lparam_assign_iface_typedef_nested3.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested3.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested3.v b/test_regress/t/t_lparam_assign_iface_typedef_nested3.v new file mode 100644 index 000000000..cb2ee3b6f --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested3.v @@ -0,0 +1,74 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// assign multiple localparams from interface typedef +// including nesting +// + +`define stop $stop +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +interface y_if #( + parameter int p_awidth = 3 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq2_t; +endinterface + +interface x_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + logic [p_dwidth-1:0] data; + } rq_t; + + typedef struct packed { + logic [p_dwidth-1:0] data; + } rs_t; + + y_if#(.p_awidth(p_awidth)) y_if0(); + +endinterface + +module top(); + x_if #( + .p_awidth(16) + ,.p_dwidth(8) + ) if0(); + + localparam p0_rq2_t = if0.y_if0.rq2_t; + localparam p0_rq_t = if0.rq_t; + localparam p0_rs_t = if0.rs_t; + + p0_rq2_t p0_rq2; + p0_rq_t p0_rq; + p0_rs_t p0_rs; + + always_comb begin + p0_rq2.addr = 16'hcafe; + p0_rq.addr = 16'hbeef; + p0_rq.data = 8'ha5; + p0_rs.data = 8'h5a; + end + + initial begin + #1; + `checkh(p0_rq2.addr, 16'hcafe); + `checkh(p0_rq.addr, 16'hbeef); + `checkh(p0_rq.data, 8'ha5); + `checkh(p0_rs.data, 8'h5a); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested4.py b/test_regress/t/t_lparam_assign_iface_typedef_nested4.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested4.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested4.v b/test_regress/t/t_lparam_assign_iface_typedef_nested4.v new file mode 100644 index 000000000..3dc4a1c77 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested4.v @@ -0,0 +1,70 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// assign multiple localparams from interface typedef +// including nesting +// + +`define stop $stop +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +interface y_if #( + parameter int p_awidth = 3 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq2_t; +endinterface + +interface x_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq_t; + + typedef struct packed { + logic [p_dwidth-1:0] data; + } rs_t; + + y_if#(.p_awidth(p_awidth)) y_if0(); +endinterface + +module top(); + x_if #( + .p_awidth(16) + ,.p_dwidth(8) + ) if0(); + + localparam type p0_rq2_t = if0.y_if0.rq2_t; + localparam type p0_rq_t = if0.rq_t; + localparam type p0_rs_t = if0.rs_t; + + p0_rq2_t p0_rq2; + p0_rq_t p0_rq; + p0_rs_t p0_rs; + + always_comb begin + p0_rq2.addr = 16'hcafe; + p0_rq.addr = 16'hbeef; + p0_rs.data = 8'h5a; + end + + initial begin + #1; + `checkh(p0_rq2.addr, 16'hcafe); + `checkh(p0_rq.addr, 16'hbeef); + `checkh(p0_rs.data, 8'h5a); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested5.py b/test_regress/t/t_lparam_assign_iface_typedef_nested5.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested5.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested5.v b/test_regress/t/t_lparam_assign_iface_typedef_nested5.v new file mode 100644 index 000000000..5d80e4f43 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested5.v @@ -0,0 +1,86 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// assign multiple localparams from interface typedef +// including nesting. param dependency. +// + +`define stop $stop +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +interface z_if #( + parameter int p_bwidth = 25 +)(); + typedef struct packed { + logic [p_bwidth-1:0] data; + } req_t; + + logic sig_a; + logic sig_b; +endinterface + +interface y_if #( + parameter int p_awidth = 3 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq2_t; +endinterface + +interface x_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq_t; + + typedef struct packed { + logic [p_dwidth-1:0] data; + } rs_t; + + y_if#(.p_awidth(p_awidth)) y_if0(); + z_if#(.p_bwidth(p_awidth+p_dwidth)) z_if0(); +endinterface + +module top(); + x_if #( + .p_awidth(16) + ,.p_dwidth(8) + ) if0(); + + localparam p0_rq2_t = if0.y_if0.rq2_t; + localparam p0_rq_t = if0.rq_t; + localparam p0_rs_t = if0.rs_t; + localparam p0_req_t = if0.z_if0.req_t; + + p0_rq2_t p0_rq2; + p0_rq_t p0_rq; + p0_rs_t p0_rs; + p0_req_t p0_req; + + always_comb begin + p0_rq2.addr = 16'hcafe; + p0_rq.addr = 16'hbeef; + p0_rs.data = 8'h5a; + p0_req.data = {p0_rq.addr, p0_rs.data}; + end + + initial begin + #1; + `checkh(p0_rq2.addr, 16'hcafe); + `checkh(p0_rq.addr, 16'hbeef); + `checkh(p0_rs.data, 8'h5a); + `checkh(p0_req.data, 24'hbeef5a); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_modules.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_modules.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules.v new file mode 100644 index 000000000..680277722 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules.v @@ -0,0 +1,38 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// localparam assignment from interface typedef with module +// hierarchy +// + +interface bus_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq_t; +endinterface + +module a_mod( + bus_if bus_io +); + localparam bus_rq_t = bus_io.rq_t; +endmodule + +module top(); + bus_if #(.p_awidth(16), .p_dwidth(8)) bus_io(); + + a_mod a_mod_inst( + .bus_io(bus_io) + ); + + initial begin + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_modules2.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules2.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules2.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_modules2.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules2.v new file mode 100644 index 000000000..d32aedfe1 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules2.v @@ -0,0 +1,63 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// localparam assignment from interface typedef with module +// hierarchy alongside localparam assignment from interface +// parameter +// + +`define stop $stop +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +interface bus_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq_t; + typedef struct packed { + logic [p_dwidth-1:0] data; + } rs_t; + + rq_t rq; + rs_t rs; +endinterface + +module a_mod( + bus_if bus_io +); + localparam bus_rq_t = bus_io.rq_t; + localparam bus_rs_t = bus_io.rs_t; + localparam p_awidth = bus_io.p_awidth; + localparam p_dwidth = bus_io.p_dwidth; + + bus_rq_t rq; + bus_rs_t rs; + + assign rs.data = 8'ha5; + assign bus_io.rs = rs; +endmodule + +module top(); + bus_if #(.p_awidth(16), .p_dwidth(8)) bus_io(); + + a_mod a_mod_inst( + .bus_io(bus_io) + ); + + initial begin + #1; + `checkh(bus_io.rs.data, 8'ha5); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_modules3.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules3.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules3.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_modules3.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules3.v new file mode 100644 index 000000000..0e02d9095 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules3.v @@ -0,0 +1,70 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// assign multiple localparams from interface typedef +// including nesting +// + +`define stop $stop +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +interface y_if #( + parameter int p_awidth = 3 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq2_t; +endinterface + +interface x_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +)(); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq_t; + + typedef struct packed { + logic [p_dwidth-1:0] data; + } rs_t; + + y_if#(.p_awidth(p_awidth)) y_if0(); +endinterface + +module top(); + x_if #( + .p_awidth(16) + ,.p_dwidth(8) + ) if0(); + + localparam p0_rq2_t = if0.y_if0.rq2_t; + localparam p0_rq_t = if0.rq_t; + localparam p0_rs_t = if0.rs_t; + + p0_rq2_t p0_rq2; + p0_rq_t p0_rq; + p0_rs_t p0_rs; + + always_comb begin + p0_rq2.addr = 16'hcafe; + p0_rq.addr = 16'hbeef; + p0_rs.data = 8'h5a; + end + + initial begin + #1; + `checkh(p0_rq2.addr, 16'hcafe); + `checkh(p0_rq.addr, 16'hbeef); + `checkh(p0_rs.data, 8'h5a); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_modules_pkg.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg.v new file mode 100644 index 000000000..9f7524fb2 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg.v @@ -0,0 +1,76 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// localparam assignment from interface typedef with module +// hierarchy. uses config struct to pass params to module and +// ultimately interface +// + +`define stop $stop +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +package a_pkg; + typedef struct packed { + int unsigned awidth; + int unsigned dwidth; + } cfg_t; +endpackage + +interface bus_if #( + parameter int p_awidth = 4 + ,parameter int p_dwidth = 7 +); + typedef struct packed { + logic [p_awidth-1:0] addr; + } rq_t; + typedef struct packed { + logic [p_dwidth-1:0] data; + } rs_t; + + rq_t rq; + rs_t rs; +endinterface + +module a_mod #(parameter a_pkg::cfg_t cfg=0)( + bus_if bus_io +); + localparam bus_rq_t = bus_io.rq_t; + localparam bus_rs_t = bus_io.rs_t; + + bus_rq_t rq; + bus_rs_t rs; + + assign rq = bus_io.rq; + assign bus_io.rs = rs; + + always_comb begin + rs.data = 8'ha5; + end +endmodule + +module top(); + localparam a_pkg::cfg_t cfg = '{ + awidth : 16 + ,dwidth : 8 + }; + bus_if #(.p_awidth(16), .p_dwidth(8)) bus_io(); + + a_mod #(cfg) a_mod_inst( + .bus_io(bus_io) + ); + + initial begin + #1; + `checkh(bus_io.rs.data, 8'ha5); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg2.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg2.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg2.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_modules_pkg2.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg2.v new file mode 100644 index 000000000..2a8343002 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg2.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// Simplified version of config struct to pass params to module +// hierarchy. This is a more compact version of the previous +// example used to debug alongside the interface typedef examples. +// + +package a_pkg; + typedef struct packed { + int unsigned awidth; + int unsigned dwidth; + } cfg_t; +endpackage + +module a_mod #(parameter a_pkg::cfg_t cfg=0)( + input logic a +); +endmodule + +module top(); + localparam a_pkg::cfg_t cfg = '{ + awidth : 16 + ,dwidth : 8 + }; + + a_mod #(cfg) a_mod_inst( + .a(1'b0) + ); + + initial begin + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg3.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg3.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg3.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_modules_pkg3.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg3.v new file mode 100644 index 000000000..7a477a510 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg3.v @@ -0,0 +1,45 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// Simplified version of config struct to pass params to module +// hierarchy. This is a more compact version of the previous +// example used to debug alongside the interface typedef examples. +// + +package a_pkg; +endpackage + +package cb; + typedef struct packed { + int unsigned XdatSize; // raw packet data size + } cfg_t; +endpackage + +module a_mod(); + typedef struct packed { + logic hdr_vld; + } cmd_meta_t; + + typedef struct packed { + cmd_meta_t meta; + } cmd_beat_t; + + typedef logic [3:0] cc_index_t; + + localparam cb::cfg_t cb_cfg = '{ + XdatSize:$bits(cmd_beat_t) + }; +endmodule + +module top(); + a_mod a_mod_inst(); + + initial begin + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg4.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg4.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg4.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_modules_pkg4.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg4.v new file mode 100644 index 000000000..5f3267ce7 --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg4.v @@ -0,0 +1,57 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// + +package aer; + typedef struct packed { + int unsigned NumCca; + } cfg_t; +endpackage + +package cca; + typedef struct packed { + int unsigned NumCc; + } cfg_t; +endpackage + +package amb; + typedef struct packed { + logic start; + } rule_t; +endpackage + +interface tb_if #(parameter cca::cfg_t cfg=0)(); + typedef logic [$clog2(cfg.NumCc)-1:0] cc_index_t; +endinterface + +module modA#( + parameter aer::cfg_t cfg=0 + // + ,localparam type rule_t = amb::rule_t +)(); + + localparam cca::cfg_t cca_cfg = '{ + NumCc : 4 + }; + + tb_if #(cca_cfg) tb_io(); + localparam type cc_index_t = tb_io.cc_index_t; +endmodule + +module tb(); + + localparam aer::cfg_t aer_cfg= '{ + NumCca : 2 + }; + + modA #(aer_cfg) modA(); + + initial begin + #1; + $finish; + end + +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg5.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg5.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg5.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_modules_pkg5.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg5.v new file mode 100644 index 000000000..72435789e --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_modules_pkg5.v @@ -0,0 +1,55 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// + +package aer; + typedef struct packed { + int unsigned NumCca; + } cfg_t; +endpackage + +package cca; + typedef struct packed { + int unsigned NumCc; + } cfg_t; +endpackage + +package amb; + typedef struct packed { + logic start; + } rule_t; +endpackage + +interface tb_if #(parameter cca::cfg_t cfg=0)(); + typedef logic [$clog2(cfg.NumCc)-1:0] cc_index_t; +endinterface + +module modA#( + localparam type rule_t = amb::rule_t +)(); + + localparam cca::cfg_t cca_cfg = '{ + NumCc : 4 + }; + + tb_if #(cca_cfg) tb_io(); + localparam type cc_index_t = tb_io.cc_index_t; +endmodule + +module tb(); + + localparam aer::cfg_t aer_cfg= '{ + NumCca : 2 + }; + + modA #() modA(); + + initial begin + #1; + $finish; + end + +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_pkg.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg.v new file mode 100644 index 000000000..f337492dd --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg.v @@ -0,0 +1,106 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// assign multiple localparams from interface typedef +// including nesting. param dependency. uses config struct +// to pass params to module hierarchy and ultimately interface +// + +`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); + +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +package a_pkg; + typedef struct packed { + int unsigned a_cfg; + int unsigned b_cfg; + int unsigned d_cfg; + } cfg_t; +endpackage + +interface z_if #( + parameter a_pkg::cfg_t CFG = 0 +)(); + typedef struct packed { + logic [CFG.d_cfg + CFG.a_cfg - 1:0] data; + } req_t; + + logic sig_a; + logic sig_b; +endinterface + +interface y_if #( + parameter a_pkg::cfg_t CFG = 0 +)(); + typedef struct packed { + logic [CFG.a_cfg-1:0] addr; + } rq2_t; +endinterface + +interface x_if #( + parameter a_pkg::cfg_t CFG = 0 +)(); + typedef struct packed { + logic [CFG.a_cfg-1:0] addr; + } rq_t; + + typedef struct packed { + logic [CFG.d_cfg-1:0] data; + } rs_t; + + y_if#(.CFG(CFG)) y_if0(); + z_if#(.CFG(CFG)) z_if0(); +endinterface + +module top(); + localparam a_pkg::cfg_t CFG = '{a_cfg: 16, b_cfg: 8, d_cfg: 8}; + + x_if #( + .CFG(CFG) + ) if0(); + + localparam p0_rq2_t = if0.y_if0.rq2_t; + localparam p0_rq_t = if0.rq_t; + localparam p0_rs_t = if0.rs_t; + localparam p0_req_t = if0.z_if0.req_t; + + p0_rq2_t p0_rq2; + p0_rq_t p0_rq; + p0_rs_t p0_rs; + p0_req_t p0_req; + + always_comb begin + p0_rq2.addr = 16'hcafe; + p0_rq.addr = 16'hbeef; + p0_rs.data = 8'h5a; + p0_req.data = {p0_rq.addr, p0_rs.data}; + end + + initial begin + #1; + `checkd($bits(p0_rq2), 16); + `checkd($bits(p0_rq), 16); + `checkd($bits(p0_rs), 8); + `checkd($bits(p0_req), 24); + `checkh(p0_rq2.addr, 16'hcafe); + `checkh(p0_rq.addr, 16'hbeef); + `checkh(p0_rs.data, 8'h5a); + `checkh(p0_req.data, 24'hbeef5a); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg2.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg2.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg2.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_pkg2.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg2.v new file mode 100644 index 000000000..95fbb5b4e --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg2.v @@ -0,0 +1,96 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// assign multiple localparams from interface typedef +// including nesting. param dependency. uses config struct +// to pass params to module hierarchy and ultimately interface +// + +`define stop $stop +`define checkh(gotv,expv) \ + do if ((gotv) !== (expv)) begin \ + $write("%%Error: %s:%0d: got=%0h exp=%0h\n", \ + `__FILE__,`__LINE__, (gotv), (expv)); \ + `stop; \ + end while(0); + +package a_pkg; + typedef struct packed { + int unsigned a_cfg; + int unsigned b_cfg; + int unsigned d_cfg; + } cfg_t; +endpackage + +interface z_if #( + parameter a_pkg::cfg_t CFG = 0 +)(); + typedef struct packed { + logic [CFG.d_cfg + CFG.a_cfg - 1:0] data; + } req_t; + + logic sig_a; + logic sig_b; +endinterface + +interface y_if #( + parameter a_pkg::cfg_t CFG = 0 +)(); + typedef struct packed { + logic [CFG.a_cfg-1:0] addr; + } rq2_t; +endinterface + +interface x_if #( + parameter a_pkg::cfg_t CFG = 0 +)(); + typedef struct packed { + logic [CFG.a_cfg-1:0] addr; + } rq_t; + + typedef struct packed { + logic [CFG.d_cfg-1:0] data; + } rs_t; + + y_if#(.CFG(CFG)) y_if0(); + z_if#(.CFG(CFG)) z_if0(); + +endinterface + +module top(); + localparam a_pkg::cfg_t CFG = '{a_cfg: 16, b_cfg: 8, d_cfg: 8}; + + x_if #( + .CFG(CFG) + ) if0(); + + localparam type p0_rq2_t = if0.y_if0.rq2_t; + localparam type p0_rq_t = if0.rq_t; + localparam type p0_rs_t = if0.rs_t; + localparam type p0_req_t = if0.z_if0.req_t; + + p0_rq2_t p0_rq2; + p0_rq_t p0_rq; + p0_rs_t p0_rs; + p0_req_t p0_req; + + always_comb begin + p0_rq2.addr = 16'hcafe; + p0_rq.addr = 16'hbeef; + p0_rs.data = 8'h5a; + p0_req.data = {p0_rq.addr, p0_rs.data}; + end + + initial begin + #1; + `checkh(p0_rq2.addr, 16'hcafe); + `checkh(p0_rq.addr, 16'hbeef); + `checkh(p0_rs.data, 8'h5a); + `checkh(p0_req.data, 24'hbeef5a); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg3.py b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg3.py new file mode 100755 index 000000000..c6e56559a --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg3.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_lparam_assign_iface_typedef_nested_pkg3.v b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg3.v new file mode 100644 index 000000000..197b9b99c --- /dev/null +++ b/test_regress/t/t_lparam_assign_iface_typedef_nested_pkg3.v @@ -0,0 +1,69 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 +// +// +`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); + +package a_pkg; + typedef struct packed { + int unsigned IdBits; + } cfg_t; +endpackage + +interface bus_if #( + parameter a_pkg::cfg_t cfg = 0 +)(); + + typedef logic [cfg.IdBits-1:0] id_t; +endinterface + +module a_mod #( + parameter int p_expect = 0 +)( + bus_if bus_io +); + + localparam type cfg_id_t = bus_io.id_t; + + cfg_id_t cfg_id; + + initial begin + #10; + `checkd($bits(cfg_id), p_expect); + end + +endmodule + +module t( + input logic clk +); + localparam a_pkg::cfg_t cfg0 = '{IdBits: 5}; + localparam a_pkg::cfg_t cfg1 = '{IdBits: 10}; + + bus_if #(.cfg(cfg0)) bus_if0(); + bus_if #(.cfg(cfg1)) bus_if1(); + + a_mod #(5) a_mod0( + .bus_io(bus_if0) + ); + + a_mod #(10) a_mod1( + .bus_io(bus_if1) + ); + + initial begin + #10; + #10; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_param_type_bad.out b/test_regress/t/t_param_type_bad.out index 4e3c370a5..68b6bf22f 100644 --- a/test_regress/t/t_param_type_bad.out +++ b/test_regress/t/t_param_type_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_param_type_bad.v:9:27: syntax error, unexpected INTEGER NUMBER +%Error: t/t_param_type_bad.v:9:27: Expecting a data type, not a constant: 2 9 | localparam type bad2 = 2; | ^ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. diff --git a/test_regress/t/t_param_type_bad3.out b/test_regress/t/t_param_type_bad3.out index f30889150..0350e5cb2 100644 --- a/test_regress/t/t_param_type_bad3.out +++ b/test_regress/t/t_param_type_bad3.out @@ -1,4 +1,4 @@ -%Error: t/t_param_type_bad3.v:9:26: Expecting a data type: 'PI' +%Error: t/t_param_type_bad3.v:9:26: Expecting a data type, not VARREF: 'PI' 9 | localparam type P_T = PI; | ^~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. diff --git a/test_regress/t/t_param_type_id_bad.out b/test_regress/t/t_param_type_id_bad.out index f90502049..a45cb5e12 100644 --- a/test_regress/t/t_param_type_id_bad.out +++ b/test_regress/t/t_param_type_id_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_param_type_id_bad.v:9:34: Expecting a data type: 'i' +%Error: t/t_param_type_id_bad.v:9:34: Expecting a data type, not VARREF: 'i' 9 | class Cls #(parameter type P_T = i); | ^ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. diff --git a/test_regress/t/t_typedef_id_bad.out b/test_regress/t/t_typedef_id_bad.out index 79f93c6d7..6bb1ef248 100644 --- a/test_regress/t/t_typedef_id_bad.out +++ b/test_regress/t/t_typedef_id_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_typedef_id_bad.v:9:34: Expecting a data type: 'i' +%Error: t/t_typedef_id_bad.v:9:34: Expecting a data type, not VARREF: 'i' 9 | class Cls #(parameter type P_T = i); | ^ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.