Fix localparam type assignment from interface type parameters (#6637) (#6732)

This commit is contained in:
em2machine 2025-12-06 08:42:59 -06:00 committed by GitHub
parent ba3794a495
commit c2cba8bfc6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 2469 additions and 79 deletions

View File

@ -276,5 +276,6 @@ Zixi Li
dependabot[bot]
february cozzocrea
sumpster
em2machine
Àlex Torregrosa
Ícaro Lima

View File

@ -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

View File

@ -291,6 +291,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3LifePost.o \
V3LinkCells.o \
V3LinkDot.o \
V3LinkDotIfaceCapture.o \
V3LinkInc.o \
V3LinkJump.o \
V3LinkLValue.o \

View File

@ -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 <algorithm>
#include <cstdlib>
#include <vector>
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 <type T>::<reference> 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<int>(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<int>(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<int>(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 <type T>::<reference> 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 <type T>::<reference> 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 <type T>::<reference> 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 <type T>::<reference> 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<std::string, AstNodeModule*> modulesToRevisit = std::move(m_modulesToRevisit);
m_lastDeferredp = nullptr;

View File

@ -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 <typename FilterFn, typename Fn>
void V3LinkDotIfaceCapture::forEachImpl(FilterFn&& filter, Fn&& fn) {
std::vector<const AstRefDType*> 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<void(const CapturedIfaceTypedef&)>& fn) {
if (!fn) return;
forEachImpl([](const CapturedIfaceTypedef&) { return true; }, fn);
}
void V3LinkDotIfaceCapture::forEachOwned(
const AstNodeModule* ownerModp, const std::function<void(const CapturedIfaceTypedef&)>& 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<bool(AstVar*, AstRefDType*)>& promoteVarCb,
const std::function<std::string()>& 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<AstCell*>(ifaceCellp));
V3LinkDotIfaceCapture::add(refp, const_cast<AstCell*>(ifaceCellp), modp, refp->typedefp(),
nullptr, ifacePortVarp);
UINFO(9, indentFn() << "iface capture capture success typedef=" << refp
<< " cell=" << ifaceCellp
<< " mod=" << (ifaceCellp->modp() ? ifaceCellp->modp()->name() : "<null>")
<< " 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());
}

View File

@ -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 <cstddef>
#include <functional>
#include <unordered_map>
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<const AstRefDType*, CapturedIfaceTypedef>;
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 <typename FilterFn, typename Fn>
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<void(const CapturedIfaceTypedef&)>& fn);
static void forEachOwned(const AstNodeModule* ownerModp,
const std::function<void(const CapturedIfaceTypedef&)>& 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<bool(AstVar*, AstRefDType*)>& promoteVarCb,
const std::function<std::string()>& indentFn);
};
#endif // VERILATOR_V3LINKDOTIFACECAPTURE_H_

View File

@ -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 <cctype>
#include <cstdlib>
#include <deque>
#include <map>
#include <memory>
@ -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()
: "<unknown>")
<< 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<int>(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()) {

View File

@ -3104,11 +3104,15 @@ list_of_param_assignments<varp>: // ==IEEE: list_of_param_assignments
;
type_assignment<varp>: // ==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($<fl>1, *$1, nullptr, $2); }
| idAny/*new-parameter*/ sigAttrListE '=' data_typeAny
{ $$ = VARDONEA($<fl>1, *$1, nullptr, $2); $$->valuep($4); }
| idAny/*new-parameter*/ sigAttrListE '=' exprOrDataType
{ $$ = VARDONEA($<fl>1, *$1, nullptr, $2);
// V3LinkParse will wrap this in RequireDType when creating ParamTypeDType
$$->valuep($4); }
;
list_of_type_assignments<varp>: // ==IEEE: list_of_type_assignments

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.