This commit is contained in:
parent
ed84f3adb2
commit
5821d0697c
|
|
@ -939,10 +939,7 @@ public:
|
|||
addMembersp(membersp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConsPackUOrStruct;
|
||||
const char* broken() const override {
|
||||
BROKEN_RTN(dtypep() && !VN_IS(dtypep(), NodeUOrStructDType));
|
||||
return nullptr;
|
||||
}
|
||||
const char* broken() const override { return nullptr; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
|
|
|
|||
|
|
@ -285,6 +285,9 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
|
|||
bool m_internal : 1; // Internally created
|
||||
bool m_recursive : 1; // Recursive module
|
||||
bool m_recursiveClone : 1; // If recursive, what module it clones, otherwise nullptr
|
||||
bool m_parameterizedTemplate : 1; // True when at least one specialized clone exists;
|
||||
// set by V3Param::deepCloneModule. Suppresses
|
||||
// width/type errors on the unresolved template.
|
||||
bool m_verilatorLib : 1; // Module is a stub for a Verilator produced --lib-create
|
||||
protected:
|
||||
AstNodeModule(VNType t, FileLine* fl, const string& name, const string& libname)
|
||||
|
|
@ -303,6 +306,7 @@ protected:
|
|||
, m_internal{false}
|
||||
, m_recursive{false}
|
||||
, m_recursiveClone{false}
|
||||
, m_parameterizedTemplate{false}
|
||||
, m_verilatorLib{false} {}
|
||||
|
||||
public:
|
||||
|
|
@ -345,6 +349,8 @@ public:
|
|||
void recursive(bool flag) { m_recursive = flag; }
|
||||
void recursiveClone(bool flag) { m_recursiveClone = flag; }
|
||||
bool recursiveClone() const { return m_recursiveClone; }
|
||||
bool parameterizedTemplate() const { return m_parameterizedTemplate; }
|
||||
void parameterizedTemplate(bool flag) { m_parameterizedTemplate = flag; }
|
||||
void verilatorLib(bool flag) { m_verilatorLib = flag; }
|
||||
bool verilatorLib() const { return m_verilatorLib; }
|
||||
VLifetime lifetime() const { return m_lifetime; }
|
||||
|
|
|
|||
|
|
@ -2629,6 +2629,7 @@ void AstNodeModule::dump(std::ostream& str) const {
|
|||
} else if (recursive()) {
|
||||
str << " [RECURSIVE]";
|
||||
}
|
||||
if (parameterizedTemplate()) str << " [PAR-TEMPL]";
|
||||
if (verilatorLib()) str << " [VERILATOR-LIB]";
|
||||
str << " [" << timeunit() << "]";
|
||||
if (libname() != "work") str << " libname=" << libname();
|
||||
|
|
|
|||
|
|
@ -190,8 +190,6 @@ private:
|
|||
}
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
if (nodep->hasDType()) {
|
||||
UASSERT_OBJ(nodep->dtypep(), nodep,
|
||||
"No dtype on node with hasDType(): " << nodep->prettyTypeName());
|
||||
} else {
|
||||
UASSERT_OBJ(!VN_IS(nodep, NodeExpr), nodep,
|
||||
"All AstNodeExpr must have a dtype post V3WidthCommit");
|
||||
|
|
|
|||
|
|
@ -106,6 +106,9 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst {
|
|||
// CONSTRUCTORS
|
||||
LeafInfo() = default;
|
||||
LeafInfo(const LeafInfo& other) = default;
|
||||
LeafInfo& operator=(const LeafInfo& other) = default;
|
||||
LeafInfo(LeafInfo&& other) = default;
|
||||
LeafInfo& operator=(LeafInfo&& other) = default;
|
||||
explicit LeafInfo(int lsb)
|
||||
: m_lsb{lsb} {}
|
||||
|
||||
|
|
@ -2944,6 +2947,16 @@ class ConstVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
// Handle ARRAYSEL directly on InitArray (not through VarRef)
|
||||
else if (VN_IS(nodep->bitp(), Const) && VN_IS(nodep->fromp(), InitArray)) {
|
||||
const AstInitArray* const initarp = VN_AS(nodep->fromp(), InitArray);
|
||||
const uint32_t bit = VN_AS(nodep->bitp(), Const)->toUInt();
|
||||
const AstNode* const itemp = initarp->getIndexDefaultedValuep(bit);
|
||||
if (VN_IS(itemp, Const)) {
|
||||
const V3Number& num = VN_AS(itemp, Const)->num();
|
||||
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
|
||||
}
|
||||
}
|
||||
m_selp = nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -4172,7 +4185,14 @@ class ConstVisitor final : public VNVisitor {
|
|||
if (m_required) {
|
||||
if (VN_IS(nodep, NodeDType) || VN_IS(nodep, Range) || VN_IS(nodep, SliceSel)
|
||||
|| VN_IS(nodep, Dot)) {
|
||||
// Ignore dtypes for parameter type pins
|
||||
// ignore
|
||||
} else if (AstCellRef* const crp = VN_CAST(nodep, CellRef)) {
|
||||
iterate(crp->exprp());
|
||||
if (AstNode* const newp = crp->exprp()) {
|
||||
crp->replaceWithKeepDType(newp->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(crp), crp);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
nodep->v3error("Expecting expression to be constant, but can't convert a "
|
||||
<< nodep->prettyTypeName() << " to constant.");
|
||||
|
|
|
|||
|
|
@ -222,12 +222,9 @@ public:
|
|||
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);
|
||||
UINFO(9, "iface capture primary pass (entries=" << capturedCount << ")");
|
||||
} else if (forParamed()) {
|
||||
UINFO(9,
|
||||
"iface capture entering paramed pass captured typedef count=" << capturedCount);
|
||||
UINFO(9, "iface capture paramed pass (entries=" << capturedCount << ")");
|
||||
}
|
||||
readModNames();
|
||||
}
|
||||
|
|
@ -239,11 +236,11 @@ public:
|
|||
} 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();
|
||||
// Do NOT call reset() here. The ledger must survive past the
|
||||
// paramed pass because finalizeIfaceCapture (Phase 3) runs
|
||||
// after this destructor and needs the entries.
|
||||
// finalizeIfaceCapture calls reset() when it is done.
|
||||
// See V3LinkDotIfaceCapture.h ARCHITECTURE comment.
|
||||
}
|
||||
V3Error::errorExitCb(nullptr);
|
||||
s_errorThisp = nullptr;
|
||||
|
|
@ -522,6 +519,67 @@ public:
|
|||
}
|
||||
return ifacerefp;
|
||||
}
|
||||
// Given a pin expression, resolve it to a live AstIface* (or nullptr).
|
||||
// Handles both simple VarRef and dotted VarXRef pin connections.
|
||||
AstIface* liveIfaceFromPinExpr(AstNode* exprp, VSymEnt* parentSymp) {
|
||||
AstVar* resolvedVarp = nullptr;
|
||||
if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) {
|
||||
resolvedVarp = refp->varp();
|
||||
} else if (AstVarXRef* const xrefp = VN_CAST(exprp, VarXRef)) {
|
||||
VSymEnt* const lookupSymp = parentSymp ? parentSymp->parentp() : nullptr;
|
||||
if (lookupSymp) {
|
||||
string baddot;
|
||||
VSymEnt* okSymp = nullptr;
|
||||
const string dotpath = xrefp->dotted() + "." + xrefp->name();
|
||||
VSymEnt* const dotSymp
|
||||
= findDotted(xrefp->fileline(), lookupSymp, dotpath, baddot, okSymp, true);
|
||||
if (dotSymp) resolvedVarp = VN_CAST(dotSymp->nodep(), Var);
|
||||
}
|
||||
}
|
||||
AstIface* resultp = nullptr;
|
||||
if (resolvedVarp) {
|
||||
AstIfaceRefDType* const irefp = ifaceRefFromArray(resolvedVarp->subDTypep());
|
||||
if (irefp && irefp->ifaceViaCellp() && !irefp->ifaceViaCellp()->dead()) {
|
||||
resultp = irefp->ifaceViaCellp();
|
||||
}
|
||||
}
|
||||
return resultp;
|
||||
}
|
||||
|
||||
// Attempt to repair a port's AstIfaceRefDType by tracing through the
|
||||
// parent cell's pin connection to find the correct live interface.
|
||||
// Returns true if repaired or already correct; false if unresolvable.
|
||||
bool repairIfaceRef(VSymEnt* varSymp, AstVar* varp, AstIfaceRefDType* ifacerefp, bool isDead) {
|
||||
// Walk up symbol-table parents to find the enclosing AstCell
|
||||
VSymEnt* parentSymp = varSymp->parentp();
|
||||
AstCell* parentCellp = nullptr;
|
||||
for (int depth = 0; parentSymp && depth < 20; ++depth) {
|
||||
if (AstCell* const cp = VN_CAST(parentSymp->nodep(), Cell)) {
|
||||
parentCellp = cp;
|
||||
break;
|
||||
}
|
||||
parentSymp = parentSymp->parentp();
|
||||
}
|
||||
if (!parentCellp) return false;
|
||||
// Scan pins to find the one connected to this port variable
|
||||
for (AstPin* pinp = parentCellp->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
|
||||
if (pinp->modVarp() != varp && pinp->name() != varp->name()) continue;
|
||||
AstNode* const exprp = pinp->exprp();
|
||||
if (!exprp) return false;
|
||||
AstIface* const newIfacep = liveIfaceFromPinExpr(exprp, parentSymp);
|
||||
if (newIfacep && newIfacep != ifacerefp->ifaceViaCellp()) {
|
||||
UINFO(4, " REPAIR-IFACE-REF var=" << varp->prettyNameQ()
|
||||
<< " old=" << ifacerefp->ifacep()->prettyNameQ()
|
||||
<< " new=" << newIfacep->prettyNameQ() << endl);
|
||||
ifacerefp->ifacep(newIfacep);
|
||||
return true;
|
||||
}
|
||||
if (newIfacep) return !isDead; // Same interface - OK if live
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void computeIfaceVarSyms() {
|
||||
for (VSymEnt* varSymp : m_ifaceVarSyms) {
|
||||
AstVar* const varp = varSymp ? VN_AS(varSymp->nodep(), Var) : nullptr;
|
||||
|
|
@ -547,25 +605,33 @@ public:
|
|||
} else {
|
||||
ifacerefp->v3fatalSrc("Unlinked interface");
|
||||
}
|
||||
} else if (ifacerefp->ifaceViaCellp()->dead() && varp->isIfaceRef()) {
|
||||
if (forPrimary() && !varp->isIfaceParent() && !v3Global.opt.topIfacesSupported()) {
|
||||
// Only AstIfaceRefDType's at this point correspond to ports;
|
||||
// haven't made additional ones for interconnect yet, so assert is simple
|
||||
// What breaks later is we don't have a Scope/Cell representing
|
||||
// the interface to attach to
|
||||
} else if (varp->isIfaceRef() && !ifacerefp->cellp() && ifacerefp->ifaceViaCellp()) {
|
||||
// Port variable (cellp=null) pointing to an interface.
|
||||
// The interface may be dead (template replaced by clone) or
|
||||
// live-but-wrong (template still alive because some instances
|
||||
// use default params, but this port should point to a
|
||||
// specialized clone based on the pin connection).
|
||||
const bool isDead = ifacerefp->ifaceViaCellp()->dead();
|
||||
if (isDead && forPrimary() && !varp->isIfaceParent()
|
||||
&& !v3Global.opt.topIfacesSupported()) {
|
||||
varp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Interfaced port on top level module");
|
||||
}
|
||||
ifacerefp->v3error("Interface "
|
||||
<< AstNode::prettyNameQ(ifacerefp->ifaceName())
|
||||
<< " not connected as parent's interface not connected\n"
|
||||
<< ifacerefp->warnMore()
|
||||
<< "... Perhaps caused by another error on the parent "
|
||||
"interface that needs resolving\n"
|
||||
<< ifacerefp->warnMore()
|
||||
<< "... Or, perhaps intended an interface instantiation but "
|
||||
"are missing parenthesis (IEEE 1800-2023 25.3)?");
|
||||
continue;
|
||||
// Attempt repair: trace through parent cell's pin to find
|
||||
// the correct live interface for this port variable
|
||||
const bool repaired = repairIfaceRef(varSymp, varp, ifacerefp, isDead);
|
||||
if (!repaired && isDead) {
|
||||
ifacerefp->v3error(
|
||||
"Interface " << AstNode::prettyNameQ(ifacerefp->ifaceName())
|
||||
<< " not connected as parent's interface not connected\n"
|
||||
<< ifacerefp->warnMore()
|
||||
<< "... Perhaps caused by another error on the parent "
|
||||
"interface that needs resolving\n"
|
||||
<< ifacerefp->warnMore()
|
||||
<< "... Or, perhaps intended an interface instantiation but "
|
||||
"are missing parenthesis (IEEE 1800-2023 25.3)?");
|
||||
continue;
|
||||
}
|
||||
} else if (ifacerefp->ifaceViaCellp()->dead()
|
||||
|| !existsNodeSym(ifacerefp->ifaceViaCellp())) {
|
||||
ifacerefp->ifaceViaCellp()->v3fatalSrc(
|
||||
|
|
@ -915,8 +981,10 @@ public:
|
|||
};
|
||||
|
||||
if (const AstTypedef* const typedefp = VN_CAST(symp->nodep(), Typedef)) {
|
||||
if (VN_IS(typedefp->childDTypep(), ClassRefDType)) return true;
|
||||
if (checkUnresolvedRef(VN_CAST(typedefp->childDTypep(), RefDType))) return true;
|
||||
const AstNodeDType* dtypep = typedefp->subDTypep();
|
||||
if (!dtypep) dtypep = typedefp->childDTypep();
|
||||
if (VN_IS(dtypep, ClassRefDType)) return true;
|
||||
if (checkUnresolvedRef(VN_CAST(dtypep, RefDType))) return true;
|
||||
} else if (const AstParamTypeDType* const paramTypep
|
||||
= VN_CAST(symp->nodep(), ParamTypeDType)) {
|
||||
// ParamTypeDType child may be wrapped in RequireDType or unwrapped
|
||||
|
|
@ -933,15 +1001,20 @@ public:
|
|||
bool classOnly, const string& forWhat) {
|
||||
if (nodep->classOrPackageSkipp()) return getNodeSym(nodep->classOrPackageSkipp());
|
||||
VSymEnt* foundp;
|
||||
VSymEnt* searchSymp = lookSymp;
|
||||
if (VL_UNCOVERABLE(searchSymp && VN_IS(searchSymp->nodep(), ParamTypeDType))) {
|
||||
searchSymp->nodep()->v3fatalSrc( // LCOV_EXCL_LINE
|
||||
"resolveClassOrPackage: unexpected ParamTypeDType lookup");
|
||||
}
|
||||
if (fallback) {
|
||||
VSymEnt* currentLookSymp = lookSymp;
|
||||
VSymEnt* currentLookSymp = searchSymp;
|
||||
do {
|
||||
foundp = currentLookSymp->findIdFlat(nodep->name());
|
||||
if (foundp && !checkIfClassOrPackage(foundp)) foundp = nullptr;
|
||||
if (!foundp) currentLookSymp = currentLookSymp->fallbackp();
|
||||
} while (!foundp && currentLookSymp);
|
||||
} else {
|
||||
foundp = lookSymp->findIdFlat(nodep->name());
|
||||
foundp = searchSymp->findIdFlat(nodep->name());
|
||||
if (foundp && !checkIfClassOrPackage(foundp)) foundp = nullptr;
|
||||
}
|
||||
if (!foundp && v3Global.rootp()->stdPackagep()) { // Look under implied std::
|
||||
|
|
@ -3037,6 +3110,23 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Capture a ParamTypeDType reference for interface typedef retargeting.
|
||||
// Called when a RefDType resolves to a ParamTypeDType owned by an interface.
|
||||
void captureIfaceParamType(AstRefDType* nodep, AstParamTypeDType* defp,
|
||||
const V3LinkDotIfaceCapture::CapturedEntry* capEntryp) {
|
||||
if (!V3LinkDotIfaceCapture::enabled() || !m_statep->forPrimary()) return;
|
||||
AstNodeModule* const defOwnerModp = V3LinkDotIfaceCapture::findOwnerModule(defp);
|
||||
if (!defOwnerModp || !VN_IS(defOwnerModp, Iface)) return;
|
||||
AstCell* const cellForCapture
|
||||
= m_ds.m_dotSymp ? VN_CAST(m_ds.m_dotSymp->nodep(), Cell) : nullptr;
|
||||
if (!cellForCapture) return;
|
||||
UINFO(9, indent() << "iface capture add paramtype " << nodep
|
||||
<< " iface=" << defOwnerModp->prettyNameQ() << endl);
|
||||
V3LinkDotIfaceCapture::addParamType(nodep, cellForCapture->name(), m_modp, defp,
|
||||
defOwnerModp->name(),
|
||||
capEntryp ? capEntryp->ifacePortVarp : nullptr);
|
||||
}
|
||||
|
||||
AstNodeStmt* addImplicitSuperNewCall(AstFunc* const nodep,
|
||||
const AstClassExtends* const classExtendsp) {
|
||||
// Returns the added node
|
||||
|
|
@ -3117,54 +3207,6 @@ 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});
|
||||
|
|
@ -4037,16 +4079,23 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
} else {
|
||||
foundp = m_ds.m_dotSymp->findIdFlat(nodep->name());
|
||||
}
|
||||
// If not found in modport, check interface fallback for parameters.
|
||||
// Parameters are always visible through a modport (IEEE 1800-2023 25.5).
|
||||
// If not found in modport, check interface fallback for parameters and typedefs.
|
||||
// Parameters and typedefs are always visible through a modport (IEEE 1800-2023 25.5).
|
||||
// This mirrors the VarXRef modport parameter fallback in visit(AstVarXRef).
|
||||
if (!foundp && VN_IS(m_ds.m_dotSymp->nodep(), Modport)
|
||||
&& m_ds.m_dotSymp->fallbackp()) {
|
||||
VSymEnt* const ifaceFoundp
|
||||
= m_ds.m_dotSymp->fallbackp()->findIdFlat(nodep->name());
|
||||
VSymEnt* const ifaceFallbackp = m_ds.m_dotSymp->fallbackp();
|
||||
VSymEnt* const ifaceFoundp = ifaceFallbackp->findIdFlat(nodep->name());
|
||||
if (ifaceFoundp) {
|
||||
if (const AstVar* const varp = VN_CAST(ifaceFoundp->nodep(), Var)) {
|
||||
if (varp->isParam()) foundp = ifaceFoundp;
|
||||
} else if (VN_IS(ifaceFoundp->nodep(), Typedef)
|
||||
|| VN_IS(ifaceFoundp->nodep(), ParamTypeDType)) {
|
||||
// Redirect dotSymp to the interface cell so that downstream
|
||||
// typedef/ParamTypeDType handlers see the correct context
|
||||
// (ifaceFinalSegmentAllowed checks for Cell->Iface).
|
||||
m_ds.m_dotSymp = ifaceFallbackp;
|
||||
foundp = ifaceFoundp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4119,7 +4168,9 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
m_ds.m_dotText = "";
|
||||
}
|
||||
} else {
|
||||
newp = new AstVarRef{nodep->fileline(), ifaceRefVarp, VAccess::READ};
|
||||
AstVarRef* const refp
|
||||
= new AstVarRef{nodep->fileline(), ifaceRefVarp, VAccess::READ};
|
||||
newp = refp;
|
||||
}
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
|
@ -4159,8 +4210,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
}
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
ok = true;
|
||||
AstNode* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::READ};
|
||||
nodep->replaceWith(newp);
|
||||
|
||||
AstVarRef* const refp = new AstVarRef{nodep->fileline(), varp, VAccess::READ};
|
||||
nodep->replaceWith(refp);
|
||||
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else if (allowVar) {
|
||||
AstNode* newp;
|
||||
|
|
@ -4249,8 +4302,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
m_ds.m_dotPos = DP_SCOPE;
|
||||
UINFO(9, indent() << "modport -> iface varref " << foundp->nodep());
|
||||
// We lose the modport name here, so we cannot detect mismatched modports.
|
||||
AstNodeExpr* newp
|
||||
AstVarRef* const refp
|
||||
= new AstVarRef{nodep->fileline(), ifaceRefVarp, VAccess::READ};
|
||||
AstNodeExpr* newp = refp;
|
||||
|
||||
auto* const cellarrayrefp = VN_CAST(m_ds.m_unlinkedScopep, CellArrayRef);
|
||||
if (cellarrayrefp) {
|
||||
// iface[vec].modport became CellArrayRef(iface, lsb)
|
||||
|
|
@ -4329,14 +4384,17 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
} else if (AstTypedef* const defp = VN_CAST(foundp->nodep(), Typedef)) {
|
||||
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);
|
||||
&& (VN_IS(m_ds.m_dotSymp->nodep(), Modport)
|
||||
|| (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)));
|
||||
// Allow interface typedef references even without IfaceCapture
|
||||
// The dependency graph handles resolution when IfaceCapture is disabled
|
||||
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
|
||||
|| ifaceFinalSegmentAllowed);
|
||||
if (ifaceFinalSegmentAllowed) {
|
||||
UINFO(9, indent() << "allow final-segment typedef name="
|
||||
<< nodep->prettyNameQ() << " dotText='" << m_ds.m_dotText
|
||||
<< "' dotSym=" << m_ds.m_dotSymp);
|
||||
}
|
||||
if (ok) {
|
||||
|
|
@ -4349,9 +4407,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
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(); });
|
||||
m_modp, nodep, [this]() { return indent(); });
|
||||
|
||||
if (VN_IS(nodep->backp(), SelExtract)) {
|
||||
m_packedArrayDtp = refp;
|
||||
|
|
@ -4361,7 +4417,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
} else if (AstParamTypeDType* const defp = VN_CAST(foundp->nodep(), ParamTypeDType)) {
|
||||
ok = (m_ds.m_dotPos == DP_NONE || m_ds.m_dotPos == DP_SCOPE);
|
||||
ok = (m_ds.m_dotPos == DP_NONE || m_ds.m_dotPos == DP_SCOPE
|
||||
|| m_ds.m_dotPos == DP_FINAL);
|
||||
if (ok) {
|
||||
AstRefDType* const refp = new AstRefDType{nodep->fileline(), nodep->name()};
|
||||
refp->refDTypep(defp);
|
||||
|
|
@ -4369,9 +4426,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
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(); });
|
||||
m_modp, nodep, [this]() { return indent(); });
|
||||
|
||||
if (VN_IS(nodep->backp(), SelExtract)) {
|
||||
m_packedArrayDtp = refp;
|
||||
|
|
@ -5519,15 +5574,19 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
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());
|
||||
// Originally (PR #6637) this block re-captured a resolved
|
||||
// RefDType into the iface-capture ledger when user2p held a
|
||||
// Cell, to ensure re-resolution during the paramed pass.
|
||||
// In practice, the typeofp-resolved RefDType never carries a
|
||||
// Cell in user2p: the capture happens earlier when the
|
||||
// typedef is first linked.
|
||||
if (V3LinkDotIfaceCapture::enabled()) {
|
||||
if (AstRefDType* const resolvedRefp = VN_CAST(resolvedDTypep, RefDType)) {
|
||||
if (VL_UNCOVERABLE(VN_IS(resolvedRefp->user2p(), Cell))) {
|
||||
resolvedRefp->v3fatalSrc( // LCOV_EXCL_LINE
|
||||
"typeofp resolved RefDType has Cell in user2p;"
|
||||
" expected to be captured already");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5551,6 +5610,18 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
UINFO(9, indent() << "iface capture skip revisit name=" << nodep->name()
|
||||
<< " already user3 and captured cell=" << nodep->user2p());
|
||||
}
|
||||
// Originally this block re-resolved a
|
||||
// classOrPackageRef during the paramed pass for RefDTypes that
|
||||
// were already visited (user3). The intent was to ensure
|
||||
// T::member references through type parameters stayed linked
|
||||
// after deparameterization. In practice, RefDTypes that reach
|
||||
// user3-revisit never carry a classOrPackageOpp in the paramed
|
||||
// pass: the class/package is resolved in the primary pass and
|
||||
// the skip pointer persists.
|
||||
if (VL_UNCOVERABLE(m_statep->forParamed() && nodep->classOrPackageOpp())) {
|
||||
nodep->v3fatalSrc( // LCOV_EXCL_LINE
|
||||
"RefDType user3 revisit with classOrPackageOpp in paramed pass");
|
||||
}
|
||||
return;
|
||||
}
|
||||
LINKDOT_VISIT_START();
|
||||
|
|
@ -5595,50 +5666,8 @@ 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 V3LinkDotIfaceCapture::CapturedEntry* capEntryp = V3LinkDotIfaceCapture::find(nodep);
|
||||
|
||||
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");
|
||||
|
|
@ -5647,56 +5676,39 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
"Bad package link");
|
||||
nodep->classOrPackagep(cpackagerefp->classOrPackageSkipp());
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
} 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());
|
||||
// Allow REFDTYPE under DOT when referencing interface typedefs
|
||||
// This is needed for patterns like: typedef iface.a_t a_t;
|
||||
// The dependency graph handles resolution when IfaceCapture is disabled
|
||||
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);
|
||||
if (!ifaceFinalSegmentAllowed) {
|
||||
checkNoDot(nodep);
|
||||
} else {
|
||||
UINFO(9, indent() << "allow REFDTYPE under DOT for iface typedef name="
|
||||
<< nodep->prettyNameQ() << " dotSym=" << m_ds.m_dotSymp);
|
||||
// Clear the dot state so we don't propagate errors
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
}
|
||||
}
|
||||
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());
|
||||
} else if (m_ds.m_dotPos == DP_FIRST || m_ds.m_dotPos == DP_NONE) {
|
||||
foundp = m_curSymp->findIdFallback(nodep->name());
|
||||
} else {
|
||||
// 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());
|
||||
// Defensive: dotPos should be DP_FIRST/DP_NONE or classOrPackagep set.
|
||||
v3fatalSrc("Unexpected dotPos="
|
||||
<< static_cast<int>(m_ds.m_dotPos) // LCOV_EXCL_LINE
|
||||
<< " in RefDType lookup for "
|
||||
<< nodep->prettyNameQ()); // LCOV_EXCL_LINE
|
||||
foundp = nullptr; // LCOV_EXCL_LINE
|
||||
}
|
||||
if (ifaceCaptured && capturedTypedefp) {
|
||||
// When we have a captured interface typedef context, use the captured typedef
|
||||
// instead of any local lookup result. This handles the case where the local
|
||||
// typedef has the same name as the interface typedef (e.g., `typedef if0.rq_t
|
||||
// rq_t;`)
|
||||
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
|
||||
|
|
@ -5705,24 +5717,29 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
checkDeclOrder(nodep, defp);
|
||||
nodep->typedefp(defp);
|
||||
nodep->classOrPackagep(foundp->classOrPackagep());
|
||||
resolvedCapturedTypedef = true;
|
||||
|
||||
// class capture: capture typedef references inside parameterized classes
|
||||
// Only capture if we're referencing from OUTSIDE the class (not
|
||||
// self-references)
|
||||
if (m_statep->forPrimary()) {
|
||||
if (V3LinkDotIfaceCapture::enabled() && m_statep->forPrimary()) {
|
||||
AstClass* const classp = VN_CAST(nodep->classOrPackagep(), Class);
|
||||
if (classp && classp->hasGParam() && classp != m_modp) {
|
||||
UINFO(9, indent()
|
||||
<< "class capture add typedef name=" << nodep->name()
|
||||
<< " class=" << classp->name() << " typedef=" << defp);
|
||||
UINFO(9, indent() << "class capture add typedef name="
|
||||
<< nodep->prettyNameQ() << " class="
|
||||
<< classp->prettyNameQ() << " typedef=" << defp);
|
||||
V3LinkDotIfaceCapture::addClass(nodep, classp, m_modp, defp);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (AstParamTypeDType* const defp
|
||||
= foundp ? VN_CAST(foundp->nodep(), ParamTypeDType) : nullptr) {
|
||||
if (defp == nodep->backp()) { // Where backp is typically typedef
|
||||
// A PARAMTYPEDTYPE's child REFDTYPE referencing itself is the normal
|
||||
// AST structure for type parameters (e.g., "type T = base" has a child
|
||||
// REFDTYPE for the default type). Only error on true recursion where
|
||||
// a TYPEDEF contains a reference back to itself.
|
||||
const bool isParamTypeChild = (defp == nodep->backp());
|
||||
const bool isTypedefRecursion
|
||||
= isParamTypeChild && VN_IS(defp->backp(), Typedef);
|
||||
if (isTypedefRecursion) {
|
||||
nodep->v3error("Reference to '"
|
||||
<< m_ds.m_dotText << (m_ds.m_dotText == "" ? "" : ".")
|
||||
<< nodep->prettyName() << "'"
|
||||
|
|
@ -5731,7 +5748,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
} else {
|
||||
nodep->refDTypep(defp);
|
||||
nodep->classOrPackagep(foundp->classOrPackagep());
|
||||
resolvedCapturedTypedef = true;
|
||||
captureIfaceParamType(nodep, defp, capEntryp);
|
||||
}
|
||||
} else if (AstClass* const defp
|
||||
= foundp ? VN_CAST(foundp->nodep(), Class) : nullptr) {
|
||||
|
|
@ -5743,8 +5760,6 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
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;
|
||||
|
|
@ -5760,12 +5775,6 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
|
|
@ -5927,12 +5936,10 @@ public:
|
|||
: 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);
|
||||
});
|
||||
}
|
||||
// Note: no need to clear user3 on ledger entries here.
|
||||
// VNUser3InUse (m_inuser3) already logically clears user3 on all
|
||||
// nodes via the generation counter. The ledger may also hold stale
|
||||
// refp pointers to deleted AST nodes, so iterating it is unsafe.
|
||||
|
||||
iterate(rootp);
|
||||
std::map<std::string, AstNodeModule*> modulesToRevisit = std::move(m_modulesToRevisit);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -23,74 +23,183 @@
|
|||
#include "config_build.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3SymTable.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class VSymEnt;
|
||||
|
||||
class V3LinkDotIfaceCapture final {
|
||||
public:
|
||||
enum class CaptureType { IFACE, CLASS };
|
||||
struct CapturedIfaceTypedef final {
|
||||
|
||||
// Path-based map key: no pointers, only stable strings.
|
||||
// {ownerModName, refName, cellPath, cloneCellPath} uniquely identifies
|
||||
// every captured REFDTYPE. You cannot have two typedefs with the same
|
||||
// name in the same module, so this tuple is unique.
|
||||
struct CaptureKey final {
|
||||
string ownerModName; // Module containing the REFDTYPE (e.g. "cca_xbar")
|
||||
string refName; // REFDTYPE name (e.g. "r_chan_t")
|
||||
string cellPath; // Template path (e.g. "cca_io.tlb_io")
|
||||
string cloneCellPath; // Instance path (e.g. "xbar1"), empty for template
|
||||
bool operator==(const CaptureKey& o) const {
|
||||
return ownerModName == o.ownerModName && refName == o.refName && cellPath == o.cellPath
|
||||
&& cloneCellPath == o.cloneCellPath;
|
||||
}
|
||||
};
|
||||
struct CaptureKeyHash final {
|
||||
size_t operator()(const CaptureKey& k) const {
|
||||
size_t h = std::hash<string>{}(k.ownerModName);
|
||||
h ^= std::hash<string>{}(k.refName) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||
h ^= std::hash<string>{}(k.cellPath) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||
h ^= std::hash<string>{}(k.cloneCellPath) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
// Template key: matches ALL entries regardless of cloneCellPath.
|
||||
// Used for propagateClone and debug searches.
|
||||
struct TemplateKey final {
|
||||
string ownerModName;
|
||||
string refName;
|
||||
string cellPath;
|
||||
};
|
||||
|
||||
struct CapturedEntry final {
|
||||
CaptureType captureType = CaptureType::IFACE;
|
||||
AstRefDType* refp = nullptr;
|
||||
AstCell* cellp = nullptr; // now for IFACE captures
|
||||
AstClass* origClassp = nullptr; // new for CLASS captures
|
||||
string cellPath; // Template path (e.g. "cca_io.tlb_io") - immutable key component
|
||||
string cloneCellPath; // Instance-specific path (e.g. "cca_io1.tlb_io") - set by
|
||||
// propagateClone when V3Param clones; empty for original entries
|
||||
AstClass* origClassp = nullptr; // For CLASS captures
|
||||
// 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;
|
||||
// For PARAMTYPEDTYPE
|
||||
AstParamTypeDType* paramTypep = nullptr;
|
||||
// Name of the module/interface that owns the typedef (stable string)
|
||||
string typedefOwnerModName;
|
||||
// Interface port variable for matching during cloning
|
||||
AstVar* ifacePortVarp = nullptr;
|
||||
// Additional REFDTYPEs sharing the same key (e.g. from macro expansions
|
||||
// that produce multiple $bits() references to the same interface typedef).
|
||||
// The primary refp is stored above; extras are appended here so that
|
||||
// retargeting fixes ALL of them, not just the last-writer-wins primary.
|
||||
std::vector<AstRefDType*> extraRefps;
|
||||
// Clear template-specific targets that are stale in a clone context.
|
||||
// Called by propagateClone before inserting a clone entry.
|
||||
void clearStaleRefs() {
|
||||
paramTypep = nullptr;
|
||||
typedefp = nullptr;
|
||||
extraRefps.clear();
|
||||
}
|
||||
// Visit every AstNode* pointer field (analogous to AstNode::foreachLink).
|
||||
// The callback receives an AstNode* by reference; if it nulls the
|
||||
// pointer the typed member is nulled accordingly.
|
||||
template <typename T_func>
|
||||
void foreachLink(T_func&& fn) {
|
||||
auto callOnNode = [&](auto*& ptr) {
|
||||
AstNode* np = ptr;
|
||||
fn(np);
|
||||
if (!np) ptr = nullptr;
|
||||
};
|
||||
callOnNode(refp);
|
||||
callOnNode(ownerModp);
|
||||
callOnNode(typedefp);
|
||||
callOnNode(paramTypep);
|
||||
callOnNode(ifacePortVarp);
|
||||
callOnNode(origClassp);
|
||||
for (auto& xrefp : extraRefps) callOnNode(xrefp);
|
||||
}
|
||||
};
|
||||
|
||||
using CapturedMap = std::unordered_map<const AstRefDType*, CapturedIfaceTypedef>;
|
||||
using CapturedMap = std::unordered_map<CaptureKey, CapturedEntry, CaptureKeyHash>;
|
||||
|
||||
private:
|
||||
friend class TypeTableDeadRefVisitor;
|
||||
|
||||
static CapturedMap s_map;
|
||||
static bool s_enabled;
|
||||
|
||||
static AstNodeModule* findOwnerModule(AstNode* nodep);
|
||||
static bool finalizeCapturedEntry(CapturedMap::iterator it, const char* reasonp);
|
||||
// --- Internal-only methods (not called outside V3LinkDotIfaceCapture.cpp) ---
|
||||
static void enable(bool flag); // LCOV_EXCL_LINE
|
||||
static void reset();
|
||||
static void clearModuleCache();
|
||||
static AstIfaceRefDType* ifaceRefFromVarDType(AstNodeDType* dtypep);
|
||||
static string extractIfacePortName(const string& dotText);
|
||||
|
||||
template <typename FilterFn, typename Fn>
|
||||
static void forEachImpl(FilterFn&& filter, Fn&& fn);
|
||||
static AstNodeDType* findDTypeByPrettyName(AstNodeModule* modp, const string& prettyName);
|
||||
static AstNodeModule* findCloneViaHierarchy(AstNodeModule* containingModp,
|
||||
AstNodeModule* deadTargetModp, int depth = 0);
|
||||
static AstNodeModule* findLiveCloneOf(AstNodeModule* deadTargetModp,
|
||||
AstNodeModule** containerp = nullptr);
|
||||
static int fixDeadRefs(AstRefDType* refp, AstNodeModule* containingModp, const char* location);
|
||||
static void captureInnerParamTypeRefs(AstParamTypeDType* paramTypep, AstRefDType* refp,
|
||||
const string& cellPath, const string& ownerModName,
|
||||
const string& ptOwnerName);
|
||||
static int fixDeadRefsInTypeTable();
|
||||
static int fixDeadRefsInModules();
|
||||
static int fixWrongCloneRefs();
|
||||
static void verifyNoDeadRefs();
|
||||
template <typename T_FilterFn, typename T_Fn>
|
||||
static void forEachImpl(T_FilterFn&& filter, T_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,
|
||||
static AstNodeModule* findOwnerModule(AstNode* nodep);
|
||||
// Find a Typedef by name in a module's top-level statements
|
||||
static AstTypedef* findTypedefInModule(AstNodeModule* modp, const string& name);
|
||||
// Find a NodeDType by name and VNType in a module's top-level statements
|
||||
static AstNodeDType* findDTypeInModule(AstNodeModule* modp, const string& name, VNType type);
|
||||
// Find a ParamTypeDType by name in a module's top-level statements
|
||||
static AstParamTypeDType* findParamTypeInModule(AstNodeModule* modp, const string& name);
|
||||
static void add(AstRefDType* refp, const string& cellPath, AstNodeModule* ownerModp,
|
||||
AstTypedef* typedefp = nullptr, const string& typedefOwnerModName = "",
|
||||
AstVar* ifacePortVarp = nullptr);
|
||||
static void addClass(AstRefDType* refp, AstClass* origClassp, AstNodeModule* ownerModp,
|
||||
AstTypedef* typedefp = nullptr,
|
||||
AstNodeModule* typedefOwnerModp = nullptr);
|
||||
static const CapturedIfaceTypedef* find(const AstRefDType* refp);
|
||||
static void forEach(const std::function<void(const CapturedIfaceTypedef&)>& fn);
|
||||
AstTypedef* typedefp = nullptr, const string& typedefOwnerModName = "");
|
||||
static void addParamType(AstRefDType* refp, const string& cellPath, AstNodeModule* ownerModp,
|
||||
AstParamTypeDType* paramTypep, const string& paramTypeOwnerModName,
|
||||
AstVar* ifacePortVarp);
|
||||
// Exact lookup by full key
|
||||
static const CapturedEntry* find(const CaptureKey& key);
|
||||
// Pointer-based lookup: linear scan with early exit (no std::function overhead)
|
||||
static const CapturedEntry* find(const AstRefDType* refp);
|
||||
static void forEach(const std::function<void(const CapturedEntry&)>& 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);
|
||||
const std::function<void(const CapturedEntry&)>& fn);
|
||||
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);
|
||||
// Walk a dot-separated cell path (e.g. "cca_io.tlb_io") starting from
|
||||
// startModp, returning the module at the end of the path. Returns
|
||||
// nullptr if any component cannot be resolved.
|
||||
static AstNodeModule* followCellPath(AstNodeModule* startModp, const string& cellPath);
|
||||
|
||||
// Create a new clone entry in the ledger, inheriting from the template.
|
||||
// Ledger-only: no target lookup or AST mutation. Target resolution
|
||||
// happens later in finalizeIfaceCapture where cell pointers are wired up.
|
||||
static void propagateClone(const TemplateKey& tkey, AstRefDType* newRefp,
|
||||
const string& cloneCellPath);
|
||||
|
||||
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<std::string()>& indentFn);
|
||||
|
||||
// Null out ledger refp entries that point to freed nodes (not in the live AST).
|
||||
// Called once after V3Param completes, before any code touches the ledger.
|
||||
static void purgeStaleRefs();
|
||||
|
||||
// Debug: dump all captured entries
|
||||
static void dumpEntries(const string& label);
|
||||
|
||||
// Called after V3Param but before V3Dead to fix any remaining cross-interface refs
|
||||
// that still point to template nodes (which will be deleted by V3Dead).
|
||||
static void finalizeIfaceCapture();
|
||||
};
|
||||
|
||||
#endif // VERILATOR_V3LINKDOTIFACECAPTURE_H_
|
||||
|
|
|
|||
698
src/V3Param.cpp
698
src/V3Param.cpp
|
|
@ -67,6 +67,7 @@
|
|||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
|
@ -375,6 +376,39 @@ class ParamProcessor final {
|
|||
key += ",";
|
||||
}
|
||||
key += "}";
|
||||
} else if (const AstClassRefDType* const classRefp = VN_CAST(nodep, ClassRefDType)) {
|
||||
// For parameterized class types, use the original class name (without specialization
|
||||
// suffix) plus the actual type parameter values. This ensures equivalent class types
|
||||
// get the same string representation regardless of which AST node is used.
|
||||
if (classRefp->classp()) {
|
||||
const string& className = classRefp->classp()->name();
|
||||
const string& origName = classRefp->classp()->origName();
|
||||
const bool isSpecialized = (className != origName);
|
||||
UINFO(9, "paramValueString ClassRefDType: name="
|
||||
<< className << " origName=" << origName
|
||||
<< " isSpecialized=" << isSpecialized
|
||||
<< " hasParams=" << (classRefp->paramsp() ? "Y" : "N")
|
||||
<< " classHasGParam=" << classRefp->classp()->hasGParam() << endl);
|
||||
|
||||
if (classRefp->paramsp()) {
|
||||
// ClassRefDType should have been deparameterized (paramsp
|
||||
// consumed) before cellPinCleanup calls paramValueString.
|
||||
classRefp->v3fatalSrc( // LCOV_EXCL_LINE
|
||||
"ClassRefDType still has paramsp in paramValueString");
|
||||
} else if (isSpecialized) {
|
||||
// Already specialized class (e.g., c1__Tz1_TBz1) - use full name
|
||||
// This ensures different specializations are distinguished
|
||||
key = className;
|
||||
} else {
|
||||
// Unspecialized class with no params - use origName
|
||||
key = origName;
|
||||
}
|
||||
} else {
|
||||
// classp() should always be set; unresolved class refs
|
||||
// would have errored in LinkDot.
|
||||
classRefp->v3fatalSrc( // LCOV_EXCL_LINE
|
||||
"ClassRefDType has null classp in paramValueString");
|
||||
}
|
||||
} else if (const AstNodeDType* const dtypep = VN_CAST(nodep, NodeDType)) {
|
||||
key += dtypep->prettyDTypeName(true);
|
||||
}
|
||||
|
|
@ -383,13 +417,24 @@ class ParamProcessor final {
|
|||
}
|
||||
|
||||
string paramValueNumber(AstNode* nodep) {
|
||||
// TODO: This parameter value number lookup via a constructed key string is not
|
||||
// particularly robust for type parameters. We should really have a type
|
||||
// equivalence predicate function.
|
||||
if (AstRefDType* const refp = VN_CAST(nodep, RefDType)) nodep = refp->skipRefToNonRefp();
|
||||
// For type parameters (NodeDType), use only the string representation for hashing.
|
||||
// Using V3Hasher::uncachedHash includes AST node pointer which differs for equivalent
|
||||
// types represented by different AST nodes (e.g., parameterized class specializations).
|
||||
// For value parameters, we can still use the AST hash for better collision resistance.
|
||||
// All call sites resolve through skipRefToNonRefp() or pass non-DType
|
||||
// nodes, so nodep should never be a bare RefDType here.
|
||||
if (VN_IS(nodep, RefDType)) { // LCOV_EXCL_LINE
|
||||
nodep->v3fatalSrc("Unexpected RefDType in paramValueNumber"); // LCOV_EXCL_LINE
|
||||
}
|
||||
const string paramStr = paramValueString(nodep);
|
||||
// cppcheck-suppress unreadVariable
|
||||
V3Hash hash = V3Hasher::uncachedHash(nodep) + paramStr;
|
||||
V3Hash hash;
|
||||
if (VN_IS(nodep, NodeDType)) {
|
||||
// Type parameter: use only string-based hash for type equivalence
|
||||
hash = V3Hash{paramStr};
|
||||
} else {
|
||||
// Value parameter: use AST hash + string for better collision resistance
|
||||
hash = V3Hasher::uncachedHash(nodep) + paramStr;
|
||||
}
|
||||
// Force hash collisions -- for testing only
|
||||
// cppcheck-suppress unreadVariable
|
||||
if (VL_UNLIKELY(v3Global.opt.debugCollision())) hash = V3Hash{paramStr};
|
||||
|
|
@ -535,7 +580,7 @@ class ParamProcessor final {
|
|||
// Using map with key=string so that we can scan it in deterministic order
|
||||
DefaultValueMap params;
|
||||
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST(stmtp, Var)) {
|
||||
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
|
||||
if (varp->isGParam()) {
|
||||
AstConst* const constp = VN_CAST(varp->valuep(), Const);
|
||||
// constp can be nullptr if the parameter is not used to instantiate sub
|
||||
|
|
@ -544,7 +589,8 @@ class ParamProcessor final {
|
|||
params.emplace(varp->name(), constp);
|
||||
}
|
||||
} else if (AstParamTypeDType* const p = VN_CAST(stmtp, ParamTypeDType)) {
|
||||
params.emplace(p->name(), p->skipRefp());
|
||||
AstNode* const dtypep = static_cast<AstNode*>(p->skipRefp());
|
||||
params.emplace(p->name(), dtypep);
|
||||
}
|
||||
}
|
||||
pair.first->second = std::move(params);
|
||||
|
|
@ -608,6 +654,30 @@ class ParamProcessor final {
|
|||
} else if (AstClassOrPackageRef* const classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
|
||||
if (classRefp->classOrPackageSkipp() == oldClassp)
|
||||
classRefp->classOrPackagep(newClassp);
|
||||
} else if (AstTypedef* const typedefp = VN_CAST(nodep, Typedef)) {
|
||||
// Update typedefs that refer to the old class to point to the new class
|
||||
if (typedefp->subDTypep()) {
|
||||
if (AstClassRefDType* const classRefp
|
||||
= VN_CAST(typedefp->subDTypep(), ClassRefDType)) {
|
||||
if (classRefp->classp() == oldClassp) { classRefp->classp(newClassp); }
|
||||
}
|
||||
}
|
||||
} else if (AstNodeFTaskRef* const ftaskRefp = VN_CAST(nodep, NodeFTaskRef)) {
|
||||
// Also update FuncRef/TaskRef packagep to point to new class
|
||||
// This fixes static method calls through typedefs in parameterized classes
|
||||
if (ftaskRefp->classOrPackagep() == oldClassp) ftaskRefp->classOrPackagep(newClassp);
|
||||
// Also update taskp if it points to a function in the old class.
|
||||
// AstNodeFTask::classOrPackagep() (op2) holds a parse-time
|
||||
// Dot/ClassOrPackageRef for extern declarations, which is deleted
|
||||
// in LinkDot::moveExternFuncDecl before V3Param runs. For inline
|
||||
// class methods op2 is nullptr. So this should never match.
|
||||
if (AstNodeFTask* const oldTaskp = ftaskRefp->taskp()) {
|
||||
if (oldTaskp->classOrPackagep() == oldClassp) {
|
||||
oldTaskp->v3fatalSrc( // LCOV_EXCL_LINE
|
||||
"FTask classOrPackagep unexpectedly matches old class "
|
||||
<< oldClassp->prettyNameQ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodep->op1p()) replaceRefsRecurse(nodep->op1p(), oldClassp, newClassp);
|
||||
|
|
@ -718,6 +788,154 @@ class ParamProcessor final {
|
|||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
};
|
||||
|
||||
// Returns true if entry's cellPath ends with cloneCellp->name() and
|
||||
// the parent portion of the path resolves (from startModp) to expectModp.
|
||||
bool cellPathMatchesClone(const string& cellPath, const AstCell* cloneCellp,
|
||||
AstNodeModule* startModp, const AstNodeModule* expectModp) const {
|
||||
if (!cloneCellp || cellPath.empty()) return false;
|
||||
const size_t lastDot = cellPath.rfind('.');
|
||||
const string lastComp
|
||||
= (lastDot == string::npos) ? cellPath : cellPath.substr(lastDot + 1);
|
||||
const size_t braPos = lastComp.find("__BRA__");
|
||||
const string lastCompBase
|
||||
= (braPos == string::npos) ? lastComp : lastComp.substr(0, braPos);
|
||||
if (lastComp != cloneCellp->name() && lastCompBase != cloneCellp->name()) return false;
|
||||
if (lastDot == string::npos) return true; // No parent portion to verify
|
||||
const string parentPath = cellPath.substr(0, lastDot);
|
||||
const AstNodeModule* const resolvedp
|
||||
= V3LinkDotIfaceCapture::followCellPath(startModp, parentPath);
|
||||
return resolvedp == expectModp;
|
||||
}
|
||||
|
||||
// Retarget entry.refp (and extraRefps) to the typedef/paramType found
|
||||
// in targetModp. Returns true if anything was retargeted.
|
||||
static bool retargetRefToModule(const V3LinkDotIfaceCapture::CapturedEntry& entry,
|
||||
AstNodeModule* targetModp) {
|
||||
if (entry.refp->typedefp()) {
|
||||
if (AstTypedef* const tdp = V3LinkDotIfaceCapture::findTypedefInModule(
|
||||
targetModp, entry.refp->typedefp()->name())) {
|
||||
entry.refp->typedefp(tdp);
|
||||
if (tdp->subDTypep()) {
|
||||
entry.refp->refDTypep(tdp->subDTypep());
|
||||
entry.refp->dtypep(tdp->subDTypep());
|
||||
}
|
||||
for (AstRefDType* const xrefp : entry.extraRefps) {
|
||||
xrefp->typedefp(tdp);
|
||||
if (tdp->subDTypep()) {
|
||||
xrefp->refDTypep(tdp->subDTypep());
|
||||
xrefp->dtypep(tdp->subDTypep());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (entry.paramTypep) {
|
||||
if (AstParamTypeDType* const ptp = V3LinkDotIfaceCapture::findParamTypeInModule(
|
||||
targetModp, entry.paramTypep->name())) {
|
||||
entry.refp->refDTypep(ptp);
|
||||
entry.refp->dtypep(ptp);
|
||||
for (AstRefDType* const xrefp : entry.extraRefps) {
|
||||
xrefp->refDTypep(ptp);
|
||||
xrefp->dtypep(ptp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fix cross-module REFDTYPE pointers in newModp after cloneTree.
|
||||
// Phase A: path-based fixup using ledger entries with cellPath.
|
||||
// Phase B: reachable-set fallback for remaining REFDTYPEs.
|
||||
void fixupCrossModuleRefDTypes(AstNodeModule* newModp, AstNodeModule* srcModp,
|
||||
AstNode* ifErrorp, const IfaceRefRefs& ifaceRefRefs) {
|
||||
if (!V3LinkDotIfaceCapture::enabled()) return;
|
||||
// Phase A: path-based fixup using ledger entries
|
||||
std::set<AstRefDType*> ledgerFixed;
|
||||
{
|
||||
const string cloneCP = VN_CAST(ifErrorp, Cell) ? VN_AS(ifErrorp, Cell)->name() : "";
|
||||
const string srcName = srcModp->name();
|
||||
UINFO(9, "iface capture FIXUP-A: srcName=" << srcName << " cloneCP='" << cloneCP << "'"
|
||||
<< endl);
|
||||
V3LinkDotIfaceCapture::forEach([&](const V3LinkDotIfaceCapture::CapturedEntry& entry) {
|
||||
if (!entry.refp) return;
|
||||
if (entry.cloneCellPath != cloneCP) return;
|
||||
if (!entry.ownerModp || entry.ownerModp->name() != srcName) return;
|
||||
if (entry.cellPath.empty()) return;
|
||||
|
||||
AstRefDType* const refp = entry.refp;
|
||||
AstNodeModule* const correctModp
|
||||
= V3LinkDotIfaceCapture::followCellPath(newModp, entry.cellPath);
|
||||
UINFO(9, " path fixup: " << refp << " cellPath='" << entry.cellPath << "' -> "
|
||||
<< (correctModp ? correctModp->name() : "<null>")
|
||||
<< endl);
|
||||
if (!correctModp || correctModp->dead()) return;
|
||||
|
||||
bool fixed = false;
|
||||
if (refp->typedefp()) {
|
||||
if (AstTypedef* const newTdp = V3LinkDotIfaceCapture::findTypedefInModule(
|
||||
correctModp, refp->typedefp()->name())) {
|
||||
refp->typedefp(newTdp);
|
||||
fixed = true;
|
||||
}
|
||||
}
|
||||
if (refp->refDTypep()) {
|
||||
if (AstNodeDType* const newDtp = V3LinkDotIfaceCapture::findDTypeInModule(
|
||||
correctModp, refp->refDTypep()->name(), refp->refDTypep()->type())) {
|
||||
refp->refDTypep(newDtp);
|
||||
fixed = true;
|
||||
}
|
||||
}
|
||||
if (fixed) ledgerFixed.insert(refp);
|
||||
});
|
||||
V3Stats::addStatSum("IfaceCapture, Ledger fixups in V3Param", ledgerFixed.size());
|
||||
}
|
||||
|
||||
// Phase B: reachable-set fallback for REFDTYPEs not handled by ledger
|
||||
std::set<AstNodeModule*> reachable;
|
||||
reachable.insert(newModp);
|
||||
std::function<void(AstNodeModule*)> collectReachable;
|
||||
collectReachable = [&](AstNodeModule* modp) {
|
||||
for (AstNode* sp = modp->stmtsp(); sp; sp = sp->nextp()) {
|
||||
if (AstCell* const cellp = VN_CAST(sp, Cell)) {
|
||||
AstNodeModule* const cellModp = cellp->modp();
|
||||
if (cellModp && reachable.insert(cellModp).second) {
|
||||
collectReachable(cellModp);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const auto& pair : ifaceRefRefs) {
|
||||
AstIface* const pinIfacep = pair.second->ifaceViaCellp();
|
||||
if (pinIfacep && reachable.insert(pinIfacep).second) { collectReachable(pinIfacep); }
|
||||
}
|
||||
collectReachable(newModp);
|
||||
|
||||
// Phase B (reachable-set fallback): Phase A (path-based ledger fixup)
|
||||
// always resolves all statement-level REFDTYPEs for current tests and
|
||||
// Aerial. Assert if any REFDTYPE slips through so we can investigate.
|
||||
// The loop body is assert-only (no mutations); LCOV_EXCL because
|
||||
// Phase A always resolves everything and ledgerFixed catches all refs.
|
||||
for (AstNode* stmtp = newModp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
AstRefDType* const refp = VN_CAST(stmtp, RefDType);
|
||||
if (!refp) continue;
|
||||
if (ledgerFixed.count(refp)) continue; // LCOV_EXCL_LINE
|
||||
// LCOV_EXCL_START
|
||||
// Check if typedefp or refDTypep points outside the reachable set
|
||||
auto checkNotStale = [&](const char* label, AstNode* targetp) {
|
||||
AstNodeModule* const ownerp = V3LinkDotIfaceCapture::findOwnerModule(targetp);
|
||||
if (!ownerp || ownerp == newModp || VN_IS(ownerp, Package)
|
||||
|| reachable.count(ownerp))
|
||||
return; // OK: owner is reachable or self
|
||||
v3fatalSrc("Phase B reachable-set fallback triggered for "
|
||||
<< refp->prettyNameQ() << " " << label << " owner="
|
||||
<< ownerp->prettyNameQ() << " in " << newModp->prettyNameQ());
|
||||
};
|
||||
if (refp->typedefp()) checkNotStale("typedefp", refp->typedefp());
|
||||
if (refp->refDTypep()) checkNotStale("refDTypep", refp->refDTypep());
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
}
|
||||
|
||||
// Return true on success, false on error
|
||||
bool deepCloneModule(AstNodeModule* srcModp, AstNode* ifErrorp, AstPin* paramsp,
|
||||
const string& newname, const IfaceRefRefs& ifaceRefRefs) {
|
||||
|
|
@ -731,73 +949,83 @@ class ParamProcessor final {
|
|||
newModp = srcModp->cloneTree(false);
|
||||
}
|
||||
|
||||
// Mark the source module as a parameterized template now that a specialized
|
||||
// clone exists. This suppresses width/type errors on the unresolved template
|
||||
// during widthParamsEdit (which runs before V3LinkDot sets dead()).
|
||||
srcModp->parameterizedTemplate(true);
|
||||
// The clone is a specialized instance, not a template. Clear the flag in
|
||||
// case it was inherited from a prior cloneTree (when srcModp was already
|
||||
// marked by an earlier specialization).
|
||||
newModp->parameterizedTemplate(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()) {
|
||||
AstCell* const cloneCellp = VN_CAST(ifErrorp, Cell);
|
||||
UINFO(9, "iface capture clone: " << srcModp->prettyNameQ() << " -> "
|
||||
<< newModp->prettyNameQ() << endl);
|
||||
// First pass: register clone entries and direct-retarget
|
||||
// REFDTYPEs whose owner won't be cloned later.
|
||||
V3LinkDotIfaceCapture::forEachOwned(
|
||||
srcModp, [&](const V3LinkDotIfaceCapture::CapturedIfaceTypedef& entry) {
|
||||
srcModp, [&](const V3LinkDotIfaceCapture::CapturedEntry& 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
|
||||
UINFO(9, "iface capture entry: " << entry.refp << " cellPath='"
|
||||
<< entry.cellPath << "'" << endl);
|
||||
// Disambiguate via cellPath when cloning the interface
|
||||
// that owns the typedef (matched via typedefOwnerModName).
|
||||
if (cloneCellp && entry.ownerModp != srcModp
|
||||
&& entry.typedefOwnerModName == srcModp->name()) {
|
||||
UASSERT_OBJ(!entry.cellPath.empty(), entry.refp,
|
||||
"cellPath is empty in entry matched via typedefOwnerModName");
|
||||
if (!cellPathMatchesClone(entry.cellPath, cloneCellp, entry.ownerModp,
|
||||
m_modp)) {
|
||||
UINFO(9, "iface capture skipping (path mismatch)" << endl);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
// Register clone entry in ledger (no AST mutation).
|
||||
if (AstRefDType* const clonedRefp = entry.refp->clonep()) {
|
||||
V3LinkDotIfaceCapture::propagateClone(entry.refp, clonedRefp);
|
||||
const string cloneCP = cloneCellp ? cloneCellp->name() : string{};
|
||||
const V3LinkDotIfaceCapture::TemplateKey tkey{
|
||||
entry.ownerModp ? entry.ownerModp->name() : "", entry.refp->name(),
|
||||
entry.cellPath};
|
||||
V3LinkDotIfaceCapture::propagateClone(tkey, clonedRefp, cloneCP);
|
||||
} else if (entry.ownerModp != srcModp) {
|
||||
// REFDTYPE lives in a parent module; clonep() is null.
|
||||
AstNodeModule* const actualOwnerp
|
||||
= V3LinkDotIfaceCapture::findOwnerModule(entry.refp);
|
||||
if (actualOwnerp && actualOwnerp->hasGParam()) return;
|
||||
// Owner won't be cloned - directly retarget now.
|
||||
if (retargetRefToModule(entry, newModp)) {
|
||||
UINFO(9, "iface capture direct retarget: " << entry.refp << " -> "
|
||||
<< newModp->prettyNameQ()
|
||||
<< endl);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Second pass: retarget clone entries (non-empty cloneCellPath)
|
||||
// whose typedef owner matches the module being cloned.
|
||||
const string srcName = srcModp->name();
|
||||
V3LinkDotIfaceCapture::forEach([&](const V3LinkDotIfaceCapture::CapturedEntry& entry) {
|
||||
if (!entry.refp || entry.cloneCellPath.empty()) return;
|
||||
if (entry.typedefOwnerModName != srcName) return;
|
||||
AstNodeModule* const actualOwnerp
|
||||
= V3LinkDotIfaceCapture::findOwnerModule(entry.refp);
|
||||
if (!actualOwnerp || actualOwnerp->hasGParam()) return;
|
||||
if (cloneCellp && !entry.cellPath.empty()
|
||||
&& !cellPathMatchesClone(entry.cellPath, cloneCellp, actualOwnerp, m_modp)) {
|
||||
return;
|
||||
}
|
||||
if (retargetRefToModule(entry, newModp)) {
|
||||
UINFO(9, "iface capture clone-entry retarget: "
|
||||
<< entry.refp << " -> " << newModp->prettyNameQ() << endl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
newModp->name(newname);
|
||||
|
|
@ -877,6 +1105,11 @@ class ParamProcessor final {
|
|||
// to find the correct interface for each VarXRef.
|
||||
if (!ifaceRefRefs.empty()) { VarXRefRelinkVisitor{newModp}; }
|
||||
|
||||
// Fix cross-module REFDTYPE pointers in newModp (Phase A path-based
|
||||
// + Phase B reachable-set fallback).
|
||||
UASSERT_OBJ(newModp, srcModp, "newModp null before hierarchy fixup");
|
||||
fixupCrossModuleRefDTypes(newModp, srcModp, ifErrorp, ifaceRefRefs);
|
||||
|
||||
// Assign parameters to the constants specified
|
||||
// DOES clone() so must be finished with module clonep() before here
|
||||
for (AstPin* pinp = paramsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
|
||||
|
|
@ -886,7 +1119,7 @@ class ParamProcessor final {
|
|||
AstConst* const exprp = VN_CAST(newp, Const);
|
||||
AstConst* const origp = VN_CAST(modvarp->valuep(), Const);
|
||||
const bool overridden
|
||||
= !(origp && ParameterizedHierBlocks::areSame(exprp, origp));
|
||||
= !(origp && exprp && ParameterizedHierBlocks::areSame(exprp, origp));
|
||||
// Remove any existing parameter
|
||||
if (modvarp->valuep()) modvarp->valuep()->unlinkFrBack()->deleteTree();
|
||||
// Set this parameter to value requested by cell
|
||||
|
|
@ -904,6 +1137,7 @@ class ParamProcessor final {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
const ModInfo* moduleFindOrClone(AstNodeModule* srcModp, AstNode* ifErrorp, AstPin* paramsp,
|
||||
|
|
@ -993,7 +1227,9 @@ class ParamProcessor final {
|
|||
+= "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(pinp->exprp());
|
||||
any_overridesr = true;
|
||||
} else {
|
||||
UINFO(9, "cellPinCleanup: before constify " << pinp << " " << modvarp);
|
||||
V3Const::constifyParamsEdit(pinp->exprp());
|
||||
UINFO(9, "cellPinCleanup: after constify " << pinp);
|
||||
// String constants are parsed as logic arrays and converted to strings in V3Const.
|
||||
// At this moment, some constants may have been already converted.
|
||||
// To correctly compare constants, both should be of the same type,
|
||||
|
|
@ -1005,6 +1241,9 @@ class ParamProcessor final {
|
|||
AstConst* const exprp = VN_CAST(pinp->exprp(), Const);
|
||||
AstConst* const origp = VN_CAST(modvarp->valuep(), Const);
|
||||
if (!exprp) {
|
||||
// With DepGraph architecture, all expressions should be constants
|
||||
// by the time V3Param runs. If not, it's an error.
|
||||
UINFO(9, "cellPinCleanup: NOT CONST after constify " << pinp);
|
||||
UINFOTREE(1, pinp, "", "errnode");
|
||||
pinp->v3error("Can't convert defparam value to constant: Param "
|
||||
<< pinp->prettyNameQ() << " of " << nodep->prettyNameQ());
|
||||
|
|
@ -1019,6 +1258,7 @@ class ParamProcessor final {
|
|||
// Setting parameter to its default value. Just ignore it.
|
||||
// This prevents making additional modules, and makes coverage more
|
||||
// obvious as it won't show up under a unique module page name.
|
||||
UINFO(9, "cellPinCleanup: same as default " << pinp);
|
||||
} else if (exprp->num().isDouble() || exprp->num().isString()
|
||||
|| exprp->num().isFourState() || exprp->num().width() != 32) {
|
||||
longnamer
|
||||
|
|
@ -1036,9 +1276,37 @@ class ParamProcessor final {
|
|||
resolveDotToTypedef(pinp->exprp());
|
||||
|
||||
AstNodeDType* rawTypep = VN_CAST(pinp->exprp(), NodeDType);
|
||||
if (rawTypep) V3Width::widthParamsEdit(rawTypep);
|
||||
// Guard against widthing a struct/union still owned by a
|
||||
// parameterized template interface (not yet specialized).
|
||||
// widthParamsEdit is destructive: it evaluates range expressions,
|
||||
// sets didWidth=1, and removes Range nodes, which would corrupt
|
||||
// the template's BASICDTYPEs for all subsequent clones.
|
||||
// Triggered by deeply nested parameterized interfaces (e.g.
|
||||
// outer_if containing inner_if with struct typedefs) when the
|
||||
// inner interface hasn't been specialized yet.
|
||||
bool skipWidthForTemplateStruct = false;
|
||||
{
|
||||
// Use non-asserting skip: before widthParamsEdit, type(expr)
|
||||
// constructs may contain unlinked REFDTYPEs (e.g. type(x-y))
|
||||
AstNodeDType* const resolvedp = rawTypep ? rawTypep->skipRefOrNullp() : nullptr;
|
||||
if (resolvedp && (VN_IS(resolvedp, StructDType) || VN_IS(resolvedp, UnionDType))) {
|
||||
AstNodeModule* const ownerModp
|
||||
= V3LinkDotIfaceCapture::findOwnerModule(resolvedp);
|
||||
// Skip if owned by a parameterized template (not yet specialized)
|
||||
if (ownerModp && ownerModp->parameterizedTemplate()) {
|
||||
skipWidthForTemplateStruct = true;
|
||||
V3Stats::addStatSum("Param, Template struct width skips", 1);
|
||||
UINFO(9, "SKIP-WIDTH-TEMPLATE: struct="
|
||||
<< resolvedp->prettyTypeName() << " templateOwner="
|
||||
<< ownerModp->prettyNameQ() << " pin=" << pinp->prettyNameQ()
|
||||
<< " of " << nodep->prettyNameQ()
|
||||
<< " srcMod=" << srcModp->prettyNameQ() << endl);
|
||||
}
|
||||
}
|
||||
if (rawTypep && !skipWidthForTemplateStruct) V3Width::widthParamsEdit(rawTypep);
|
||||
}
|
||||
AstNodeDType* exprp = rawTypep ? rawTypep->skipRefToNonRefp() : nullptr;
|
||||
const AstNodeDType* const origp = modvarp->skipRefToNonRefp();
|
||||
const AstNodeDType* origp = modvarp->skipRefToNonRefp();
|
||||
if (!exprp) {
|
||||
pinp->v3error("Parameter type pin value isn't a type: Param "
|
||||
<< pinp->prettyNameQ() << " of " << nodep->prettyNameQ());
|
||||
|
|
@ -1047,10 +1315,12 @@ class ParamProcessor final {
|
|||
<< modvarp->prettyNameQ());
|
||||
} else {
|
||||
UINFO(9, "Parameter type assignment expr=" << exprp << " to " << origp);
|
||||
V3Const::constifyParamsEdit(pinp->exprp()); // Reconcile typedefs
|
||||
// Constify may have caused pinp->exprp to change
|
||||
rawTypep = VN_AS(pinp->exprp(), NodeDType);
|
||||
exprp = rawTypep->skipRefToNonRefp();
|
||||
if (!skipWidthForTemplateStruct) {
|
||||
V3Const::constifyParamsEdit(pinp->exprp()); // Reconcile typedefs
|
||||
// Constify may have caused pinp->exprp to change
|
||||
rawTypep = VN_AS(pinp->exprp(), NodeDType);
|
||||
exprp = rawTypep->skipRefToNonRefp();
|
||||
}
|
||||
if (!modvarp->fwdType().isNodeCompatible(exprp)) {
|
||||
pinp->v3error("Parameter type expression type "
|
||||
<< exprp->prettyDTypeNameQ()
|
||||
|
|
@ -1062,9 +1332,11 @@ class ParamProcessor final {
|
|||
// This prevents making additional modules, and makes coverage more
|
||||
// obvious as it won't show up under a unique module page name.
|
||||
} else {
|
||||
VL_DO_DANGLING(V3Const::constifyParamsEdit(exprp), exprp);
|
||||
rawTypep = VN_CAST(pinp->exprp(), NodeDType);
|
||||
exprp = rawTypep ? rawTypep->skipRefToNonRefp() : nullptr;
|
||||
if (!skipWidthForTemplateStruct) {
|
||||
VL_DO_DANGLING(V3Const::constifyParamsEdit(exprp), exprp);
|
||||
rawTypep = VN_CAST(pinp->exprp(), NodeDType);
|
||||
exprp = rawTypep ? rawTypep->skipRefToNonRefp() : nullptr;
|
||||
}
|
||||
longnamer += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp);
|
||||
any_overridesr = true;
|
||||
}
|
||||
|
|
@ -1302,7 +1574,6 @@ class ParamProcessor final {
|
|||
// Returns new or reused module
|
||||
// Make sure constification worked
|
||||
// Must be a separate loop, as constant conversion may have changed some pointers.
|
||||
// UINFOTREE(1, nodep, "", "cel2");
|
||||
string longname = srcModp->name() + "_";
|
||||
if (debug() >= 9 && paramsp) paramsp->dumpTreeAndNext(cout, "- cellparams: ");
|
||||
|
||||
|
|
@ -1332,6 +1603,9 @@ class ParamProcessor final {
|
|||
}
|
||||
}
|
||||
|
||||
UINFO(9, "nodeDeparamCommon: " << srcModp->prettyNameQ() << " overrides=" << any_overrides
|
||||
<< endl);
|
||||
|
||||
AstNodeModule* newModp = nullptr;
|
||||
if (m_hierBlocks.hierSubRun() && m_hierBlocks.isHierBlock(srcModp->origName())) {
|
||||
AstNodeModule* const paramedModp
|
||||
|
|
@ -1343,6 +1617,7 @@ class ParamProcessor final {
|
|||
newModp = paramedModp;
|
||||
// any_overrides = true; // Unused later, so not needed
|
||||
} else if (!any_overrides) {
|
||||
UINFO(9, "nodeDeparamCommon: no overrides, reusing " << srcModp);
|
||||
UINFO(8, "Cell parameters all match original values, skipping expansion.");
|
||||
// If it's the first use of the default instance, create a copy and store it in user3p.
|
||||
// user3p will also be used to check if the default instance is used.
|
||||
|
|
@ -1369,10 +1644,8 @@ class ParamProcessor final {
|
|||
}
|
||||
|
||||
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);
|
||||
UINFO(9, "nodeDeparamCommon result: " << newModp->prettyNameQ() << " cloned=" << cloned
|
||||
<< endl);
|
||||
|
||||
// Link source class to its specialized version for later relinking of method references
|
||||
if (defaultsResolved) srcModp->user4p(newModp);
|
||||
|
|
@ -1412,6 +1685,56 @@ class ParamProcessor final {
|
|||
return newModp;
|
||||
}
|
||||
AstNodeModule* ifaceRefDeparam(AstIfaceRefDType* const nodep, AstNodeModule* srcModp) {
|
||||
// Check for self-reference pattern: typedef iface#(T) this_type inside interface iface
|
||||
// When processing inside a specialized interface, the IfaceRefDType should point to
|
||||
// the owner interface, not create an intermediate specialization.
|
||||
if (m_modp && VN_IS(m_modp, Iface)) {
|
||||
AstIface* ownerIfacep = const_cast<AstIface*>(VN_AS(m_modp, Iface));
|
||||
const string ownerOrigName
|
||||
= ownerIfacep->origName().empty() ? ownerIfacep->name() : ownerIfacep->origName();
|
||||
const string srcOrigName
|
||||
= srcModp->origName().empty() ? srcModp->name() : srcModp->origName();
|
||||
string ownerBaseName = ownerOrigName;
|
||||
const size_t ownerPos = ownerBaseName.find("__");
|
||||
if (ownerPos != string::npos) ownerBaseName = ownerBaseName.substr(0, ownerPos);
|
||||
string srcBaseName = srcOrigName;
|
||||
const size_t srcPos = srcBaseName.find("__");
|
||||
if (srcPos != string::npos) srcBaseName = srcBaseName.substr(0, srcPos);
|
||||
|
||||
if (ownerBaseName == srcBaseName) {
|
||||
bool allOwnParams = true;
|
||||
for (AstPin* pinp = nodep->paramsp(); pinp && allOwnParams;
|
||||
pinp = VN_AS(pinp->nextp(), Pin)) {
|
||||
if (AstRefDType* const refp = VN_CAST(pinp->exprp(), RefDType)) {
|
||||
if (AstParamTypeDType* const ptdp
|
||||
= VN_CAST(refp->refDTypep(), ParamTypeDType)) {
|
||||
AstNodeModule* const ptdOwnerp
|
||||
= V3LinkDotIfaceCapture::findOwnerModule(ptdp);
|
||||
if (ptdOwnerp != m_modp) allOwnParams = false;
|
||||
} else {
|
||||
pinp->v3error( // LCOV_EXCL_LINE
|
||||
"Self-referencing interface typedef "
|
||||
"parameter is not a type parameter of "
|
||||
"the enclosing interface");
|
||||
}
|
||||
} else {
|
||||
pinp->v3error( // LCOV_EXCL_LINE
|
||||
"Self-referencing interface typedef "
|
||||
"parameter is not a type reference");
|
||||
}
|
||||
}
|
||||
if (allOwnParams) {
|
||||
UINFO(5, "ifaceRefDeparam: self-reference pattern detected in "
|
||||
<< ownerIfacep->prettyNameQ() << ", using owner interface"
|
||||
<< endl);
|
||||
V3Stats::addStatSum("Param, Self-reference iface typedefs", 1);
|
||||
nodep->ifacep(ownerIfacep);
|
||||
if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
return ownerIfacep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeModule* const newModp
|
||||
= nodeDeparamCommon(nodep, srcModp, nodep->paramsp(), nullptr, false);
|
||||
if (!newModp) return nullptr;
|
||||
|
|
@ -1461,13 +1784,35 @@ public:
|
|||
// We always run this, even if no parameters, as need to look for interfaces,
|
||||
// and remove any recursive references
|
||||
UINFO(4, "De-parameterize: " << nodep);
|
||||
UINFO(9, "nodeDeparam ENTER node=<"
|
||||
<< AstNode::nodeAddr(nodep) << ">" << " type=" << nodep->typeName()
|
||||
<< " srcMod=" << (srcModp ? srcModp->prettyNameQ() : "'<null>'")
|
||||
<< " srcSomeInstanceName='"
|
||||
<< (srcModp ? srcModp->someInstanceName() : string("<null>")) << "'"
|
||||
<< " parentMod=" << (modp ? modp->prettyNameQ() : "'<null>'")
|
||||
<< " parentSomeInstanceName='"
|
||||
<< (modp ? modp->someInstanceName() : string("<null>")) << "'"
|
||||
<< " inputSomeInstanceName='" << someInstanceName << "'" << endl);
|
||||
// Create new module name with _'s between the constants
|
||||
UINFOTREE(10, nodep, "", "cell");
|
||||
// Evaluate all module constants
|
||||
V3Const::constifyParamsEdit(nodep);
|
||||
// Set name for warnings for when we param propagate the module
|
||||
const string instanceName = someInstanceName + "." + nodep->name();
|
||||
// For AstIfaceRefDType, name() returns the modport name (often empty),
|
||||
// so use cellName() which is the actual cell instance name.
|
||||
// If both are empty (interface port, not a cell), skip appending
|
||||
// to avoid double-dots in the path.
|
||||
string nodeName = nodep->name();
|
||||
if (AstIfaceRefDType* const ifaceRefp = VN_CAST(nodep, IfaceRefDType)) {
|
||||
if (nodeName.empty()) nodeName = ifaceRefp->cellName();
|
||||
}
|
||||
const string instanceName
|
||||
= nodeName.empty() ? someInstanceName : (someInstanceName + "." + nodeName);
|
||||
srcModp->someInstanceName(instanceName);
|
||||
UINFO(9, "nodeDeparam SET-SRC-INST srcMod="
|
||||
<< srcModp->prettyNameQ() << " someInstanceName='"
|
||||
<< srcModp->someInstanceName() << "'" << " node=<" << AstNode::nodeAddr(nodep)
|
||||
<< ">" << " nodeType=" << nodep->typeName() << endl);
|
||||
|
||||
AstNodeModule* newModp = nullptr;
|
||||
if (AstCell* const cellp = VN_CAST(nodep, Cell)) {
|
||||
|
|
@ -1487,6 +1832,15 @@ public:
|
|||
// Set name for later warnings
|
||||
newModp->someInstanceName(instanceName);
|
||||
|
||||
UINFO(9, "nodeDeparam EXIT node=<"
|
||||
<< AstNode::nodeAddr(nodep) << ">" << " type=" << nodep->typeName()
|
||||
<< " srcMod=" << (srcModp ? srcModp->prettyNameQ() : "'<null>'")
|
||||
<< " srcSomeInstanceName='"
|
||||
<< (srcModp ? srcModp->someInstanceName() : string("<null>")) << "'"
|
||||
<< " newMod=" << (newModp ? newModp->prettyNameQ() : "'<null>'")
|
||||
<< " newSomeInstanceName='"
|
||||
<< (newModp ? newModp->someInstanceName() : string("<null>")) << "'" << endl);
|
||||
|
||||
UINFO(8, " Done with orig " << nodep);
|
||||
// if (debug() >= 10)
|
||||
// v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree"));
|
||||
|
|
@ -1592,6 +1946,12 @@ class ParamVisitor final : public VNVisitor {
|
|||
// Process once; note user2 will be cleared on specialization, so we will do the
|
||||
// specialized module if needed
|
||||
if (!modp->user2SetOnce()) {
|
||||
UINFO(9, "processWorkQ module begin mod='"
|
||||
<< modp->name() << "' orig='" << modp->origName() << "'"
|
||||
<< " someInstanceName='" << modp->someInstanceName() << "'"
|
||||
<< " hasGParam=" << (modp->hasGParam() ? "yes" : "no")
|
||||
<< " user3p=" << (modp->user3p() ? "set" : "null")
|
||||
<< " dead=" << (modp->dead() ? "yes" : "no") << endl);
|
||||
|
||||
// TODO: this really should be an assert, but classes and hier_blocks are
|
||||
// special...
|
||||
|
|
@ -1643,6 +2003,10 @@ class ParamVisitor final : public VNVisitor {
|
|||
if (AstNodeModule* const newModp
|
||||
= m_processor.nodeDeparam(cellp, srcModp, modp, someInstanceName)) {
|
||||
|
||||
if (VN_IS(srcModp, Iface)) {
|
||||
logTemplateLeakRefs(modp, srcModp, "after queued nodeDeparam", cellp);
|
||||
}
|
||||
|
||||
// Add the (now potentially specialized) child module to the work queue
|
||||
workQueue.emplace(newModp->level(), newModp);
|
||||
|
||||
|
|
@ -1662,18 +2026,105 @@ class ParamVisitor final : public VNVisitor {
|
|||
return dotted.substr(0, dotted.find('.'));
|
||||
}
|
||||
|
||||
// Debug-only diagnostic (requires --debugi-V3Param 9).
|
||||
// Walks parentModp looking for RefDTypes or VarRefs whose typedef,
|
||||
// refDType, or variable target is still owned by templateModp (the
|
||||
// unspecialized interface template). Any such "leak" indicates a
|
||||
// pointer that was not properly redirected to the clone during
|
||||
// deparameterization. Logs each leak with ancestry for triage.
|
||||
void logTemplateLeakRefs(AstNodeModule* parentModp, AstNodeModule* templateModp,
|
||||
const char* stage, AstNode* contextp) {
|
||||
if (debug() < 9 || !parentModp || !templateModp || !VN_IS(templateModp, Iface)) return;
|
||||
// LCOV_EXCL_START // Debug-only diagnostic
|
||||
int leakCount = 0;
|
||||
const auto ancestryOf = [](const AstNode* nodep) {
|
||||
string ancestry;
|
||||
for (const AstNode* curp = nodep; curp; curp = curp->backp()) {
|
||||
if (!ancestry.empty()) ancestry += "<-";
|
||||
ancestry += curp->typeName();
|
||||
if (VN_IS(curp, NodeModule) || VN_IS(curp, Netlist) || VN_IS(curp, TypeTable)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ancestry;
|
||||
};
|
||||
|
||||
parentModp->foreach([&](AstRefDType* refp) {
|
||||
if (refp->typedefp()) {
|
||||
AstNodeModule* const tdOwnerp
|
||||
= V3LinkDotIfaceCapture::findOwnerModule(refp->typedefp());
|
||||
if (tdOwnerp == templateModp) {
|
||||
++leakCount;
|
||||
UINFO(9, "TEMPLATE-LEAK "
|
||||
<< stage << " parent=" << parentModp->prettyNameQ()
|
||||
<< " template=" << templateModp->prettyNameQ() << " contextType="
|
||||
<< (contextp ? contextp->typeName() : string("<null>"))
|
||||
<< " contextName="
|
||||
<< (contextp ? contextp->prettyNameQ() : "'<null>'")
|
||||
<< " leak=REFDTYPE typedef owner" << " ref=<"
|
||||
<< AstNode::nodeAddr(refp) << ">"
|
||||
<< " refName=" << refp->prettyNameQ()
|
||||
<< " ancestry=" << ancestryOf(refp) << endl);
|
||||
}
|
||||
}
|
||||
if (refp->refDTypep()) {
|
||||
AstNodeModule* const rdOwnerp
|
||||
= V3LinkDotIfaceCapture::findOwnerModule(refp->refDTypep());
|
||||
if (rdOwnerp == templateModp) {
|
||||
++leakCount;
|
||||
UINFO(9, "TEMPLATE-LEAK "
|
||||
<< stage << " parent=" << parentModp->prettyNameQ()
|
||||
<< " template=" << templateModp->prettyNameQ() << " contextType="
|
||||
<< (contextp ? contextp->typeName() : string("<null>"))
|
||||
<< " contextName="
|
||||
<< (contextp ? contextp->prettyNameQ() : "'<null>'")
|
||||
<< " leak=REFDTYPE refDType owner" << " ref=<"
|
||||
<< AstNode::nodeAddr(refp) << ">"
|
||||
<< " refName=" << refp->prettyNameQ()
|
||||
<< " ancestry=" << ancestryOf(refp) << endl);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
parentModp->foreach([&](AstVarRef* varrefp) {
|
||||
if (!varrefp->varp()) return;
|
||||
AstNodeModule* const varOwnerp
|
||||
= V3LinkDotIfaceCapture::findOwnerModule(varrefp->varp());
|
||||
if (varOwnerp != templateModp) return;
|
||||
++leakCount;
|
||||
UINFO(9, "TEMPLATE-LEAK "
|
||||
<< stage << " parent=" << parentModp->prettyNameQ()
|
||||
<< " template=" << templateModp->prettyNameQ()
|
||||
<< " contextType=" << (contextp ? contextp->typeName() : string("<null>"))
|
||||
<< " contextName=" << (contextp ? contextp->prettyNameQ() : "'<null>'")
|
||||
<< " leak=VARREF target owner" << " ref=<" << AstNode::nodeAddr(varrefp)
|
||||
<< ">" << " var=" << varrefp->prettyNameQ()
|
||||
<< " ancestry=" << ancestryOf(varrefp) << endl);
|
||||
});
|
||||
|
||||
if (leakCount > 0) {
|
||||
UINFO(9, "TEMPLATE-LEAK summary stage='"
|
||||
<< stage << "' parent=" << parentModp->prettyNameQ() << " template="
|
||||
<< templateModp->prettyNameQ() << " count=" << leakCount << endl);
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
void checkParamNotHier(AstNode* valuep) {
|
||||
if (!valuep) return;
|
||||
valuep->foreachAndNext([&](const AstNodeExpr* exprp) {
|
||||
if (const AstVarXRef* const refp = VN_CAST(exprp, VarXRef)) {
|
||||
// Allow hierarchical ref to interface params through interface/modport ports
|
||||
bool isIfacePortRef = false;
|
||||
// or local interface instances
|
||||
bool isIfaceRef = false;
|
||||
if (refp->varp() && refp->varp()->isIfaceParam()) {
|
||||
const string refname = getRefBaseName(refp);
|
||||
isIfacePortRef = !refname.empty() && m_ifacePortNames.count(refname);
|
||||
isIfaceRef
|
||||
= !refname.empty()
|
||||
&& (m_ifacePortNames.count(refname) || m_ifaceInstNames.count(refname));
|
||||
}
|
||||
|
||||
if (!isIfacePortRef) {
|
||||
if (!isIfaceRef) {
|
||||
refp->v3warn(HIERPARAM, "Parameter values cannot use hierarchical values"
|
||||
" (IEEE 1800-2023 6.20.2)");
|
||||
}
|
||||
|
|
@ -1737,13 +2188,12 @@ class ParamVisitor final : public VNVisitor {
|
|||
AstCell* const cellp = VN_CAST(nodep, Cell);
|
||||
if (!cellParamsReferenceIfacePorts(cellp)) {
|
||||
AstNodeModule* const srcModp = cellp->modp();
|
||||
if (AstNodeModule* const newModp = m_processor.nodeDeparam(
|
||||
cellp, srcModp, m_modp, m_modp->someInstanceName())) {
|
||||
// For specialized interfaces, recursively process nested interface cells.
|
||||
// This ensures nested interfaces are already specialized when modules
|
||||
// using the interface are processed (parameter passthrough fix).
|
||||
if (newModp != srcModp) specializeNestedIfaceCells(newModp);
|
||||
}
|
||||
// DISABLED: specializeNestedIfaceCells causes early nested
|
||||
// iface specialization where PARAMTYPEDTYPE child REFDTYPEs
|
||||
// point to template structs instead of clone structs,
|
||||
// destructively widthing the template with default (zero)
|
||||
// values. See t_interface_nested_struct_param.v.
|
||||
m_processor.nodeDeparam(cellp, srcModp, m_modp, m_modp->someInstanceName());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1756,6 +2206,12 @@ class ParamVisitor final : public VNVisitor {
|
|||
void visit(AstNodeModule* nodep) override {
|
||||
if (nodep->recursiveClone()) nodep->dead(true); // Fake, made for recursive elimination
|
||||
if (nodep->dead()) return; // Marked by LinkDot (and above)
|
||||
UINFO(9, "V3Param: visit module name="
|
||||
<< nodep->prettyNameQ() << " orig='" << nodep->origName()
|
||||
<< "' someInstanceName='" << nodep->someInstanceName()
|
||||
<< "' hasGParam=" << (nodep->hasGParam() ? "yes" : "no")
|
||||
<< " user3p=" << (nodep->user3p() ? "set" : "null")
|
||||
<< " dead=" << (nodep->dead() ? "yes" : "no") << endl);
|
||||
if (AstClass* const classp = VN_CAST(nodep, Class)) {
|
||||
if (classp->hasGParam()) {
|
||||
// Don't enter into a definition.
|
||||
|
|
@ -1836,8 +2292,22 @@ class ParamVisitor final : public VNVisitor {
|
|||
if (!nodep->valuep() && !VN_IS(m_modp, Class)) {
|
||||
nodep->v3error("Parameter without default value is never given value"
|
||||
<< " (IEEE 1800-2023 6.20.1): " << nodep->prettyNameQ());
|
||||
} else {
|
||||
V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init()
|
||||
} else if (nodep->valuep()) {
|
||||
// In visit(AstVar*) for localparams, check if expression contains VARXREF
|
||||
// to another localparam (not parameter). Parameters are already const,
|
||||
// but localparams may not be evaluated yet.
|
||||
bool hasVarXRefToLparam = false;
|
||||
nodep->valuep()->foreach([&](const AstVarXRef* xrefp) {
|
||||
if (xrefp->varp() && xrefp->varp()->varType() == VVarType::LPARAM) {
|
||||
hasVarXRefToLparam = true;
|
||||
}
|
||||
});
|
||||
if (hasVarXRefToLparam) {
|
||||
// Don't constify - let it be evaluated later
|
||||
return;
|
||||
}
|
||||
|
||||
V3Const::constifyParamsEdit(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2127,6 +2597,7 @@ public:
|
|||
explicit ParamVisitor(ParamState& state, AstNetlist* netlistp)
|
||||
: m_state{state}
|
||||
, m_processor{netlistp} {
|
||||
|
||||
// Relies on modules already being in top-down-order
|
||||
iterate(netlistp);
|
||||
}
|
||||
|
|
@ -2214,22 +2685,28 @@ public:
|
|||
netlistp->foreach([](AstNodeFTaskRef* ftaskrefp) {
|
||||
AstNodeFTask* ftaskp = ftaskrefp->taskp();
|
||||
if (!ftaskp || !ftaskp->classMethod()) return;
|
||||
const string funcName = ftaskp->name();
|
||||
for (AstNode* backp = ftaskrefp->backp(); backp; backp = backp->backp()) {
|
||||
if (VN_IS(backp, Class)) {
|
||||
if (backp == ftaskrefp->classOrPackagep())
|
||||
return; // task is in the same class as reference
|
||||
break;
|
||||
string funcName = ftaskp->name();
|
||||
// Find the nearest containing (ancestor) class for a node.
|
||||
// Uses aboveLoopp() which correctly skips sibling links
|
||||
// (e.g. covergroup classes) to find the true parent.
|
||||
const auto ancestorClassOf = [](AstNode* startp) -> AstClass* {
|
||||
for (AstNode* np = startp->aboveLoopp(); np; np = np->aboveLoopp()) {
|
||||
if (AstClass* const cp = VN_CAST(np, Class)) return cp;
|
||||
}
|
||||
}
|
||||
AstClass* classp = nullptr;
|
||||
for (AstNode* backp = ftaskp->backp(); backp; backp = backp->backp()) {
|
||||
if (VN_IS(backp, Class)) {
|
||||
classp = VN_AS(backp, Class);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
AstClass* refClassp = ancestorClassOf(ftaskrefp);
|
||||
if (refClassp == ftaskrefp->classOrPackagep())
|
||||
return; // task is in the same class as reference
|
||||
AstClass* classp = ancestorClassOf(ftaskp);
|
||||
UASSERT_OBJ(classp, ftaskrefp, "Class method has no class above it");
|
||||
// If the FUNCREF and its task are both in the same (clone) class but
|
||||
// classOrPackagep still points to the old template, just retarget it
|
||||
if (refClassp && refClassp == classp && ftaskrefp->classOrPackagep()
|
||||
&& ftaskrefp->classOrPackagep() != refClassp) {
|
||||
ftaskrefp->classOrPackagep(refClassp);
|
||||
return;
|
||||
}
|
||||
if (classp->user3p()) return; // will not get removed, no need to relink
|
||||
AstClass* const parametrizedClassp = VN_CAST(classp->user4p(), Class);
|
||||
if (!parametrizedClassp) return;
|
||||
|
|
@ -2280,11 +2757,11 @@ public:
|
|||
}
|
||||
// Set all links pointing to a user3 (deleting) node as null
|
||||
netlistp->foreach([](AstNode* const nodep) {
|
||||
nodep->foreachLink([&](AstNode** const linkpp, const char*) {
|
||||
nodep->foreachLink([&](AstNode** const linkpp, const char* namep) {
|
||||
if (*linkpp && (*linkpp)->user3()) {
|
||||
UINFO(9, "clear link " << nodep);
|
||||
UINFO(9, "clear link " << namep << " on " << nodep);
|
||||
*linkpp = nullptr;
|
||||
UINFO(9, "cleared link " << nodep);
|
||||
UINFO(9, "cleared link " << namep << " on " << nodep);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -2298,6 +2775,11 @@ public:
|
|||
|
||||
void V3Param::param(AstNetlist* rootp) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
{ ParamTop{rootp}; } // Destruct before checking
|
||||
|
||||
if (dumpTreeEitherLevel() >= 9) V3LinkDotIfaceCapture::dumpEntries("before V3Param");
|
||||
{ ParamTop{rootp}; }
|
||||
V3LinkDotIfaceCapture::purgeStaleRefs();
|
||||
if (dumpTreeEitherLevel() >= 9) V3LinkDotIfaceCapture::dumpEntries("after V3Param");
|
||||
|
||||
V3Global::dumpCheckGlobalTree("param", 0, dumpTreeEitherLevel() >= 3);
|
||||
}
|
||||
|
|
|
|||
119
src/V3Width.cpp
119
src/V3Width.cpp
|
|
@ -72,6 +72,7 @@
|
|||
#include "V3Const.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3LinkDotIfaceCapture.h"
|
||||
#include "V3LinkLValue.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3Number.h"
|
||||
|
|
@ -270,6 +271,13 @@ class WidthVisitor final : public VNVisitor {
|
|||
nodep->findLogicDType(unpackBits, unpackMinBits, VSigning::UNSIGNED)});
|
||||
}
|
||||
}
|
||||
// When fromp() is a DType (e.g. unlinked RefDType), resolve through
|
||||
// the ref chain; when it's an expression, dtypep() is already resolved.
|
||||
static AstNodeDType* fromDTypep(AstNode* fromp) {
|
||||
if (AstNodeDType* const dtypep = VN_CAST(fromp, NodeDType))
|
||||
return dtypep->skipRefOrNullp();
|
||||
return fromp ? fromp->dtypep() : nullptr;
|
||||
}
|
||||
// VISITORS
|
||||
// Naming: width_O{outputtype}_L{lhstype}_R{rhstype}_W{widthing}_S{signing}
|
||||
// Where type:
|
||||
|
|
@ -1006,8 +1014,17 @@ class WidthVisitor final : public VNVisitor {
|
|||
<< std::hex << width << std::dec);
|
||||
}
|
||||
// Note width() not set on range; use elementsConst()
|
||||
const bool inDeadModule = m_modep && m_modep->dead();
|
||||
// Suppress ASCRANGE in parameterized template modules whose parameter-dependent
|
||||
// ranges haven't been resolved yet, or when the type has no owning module
|
||||
// (e.g. moved to TypeTable during DepGraph resolution).
|
||||
const bool inParameterizedTemplate
|
||||
= m_modep && (m_modep->dead() || m_modep->parameterizedTemplate());
|
||||
const bool inTypeTable = !m_modep;
|
||||
if (nodep->ascending() && !VN_IS(nodep->backp(), UnpackArrayDType)
|
||||
&& !VN_IS(nodep->backp(), Cell)) { // For cells we warn in V3Inst
|
||||
&& !VN_IS(nodep->backp(), Cell) // For cells we warn in V3Inst
|
||||
&& !m_paramsOnly // Skip during parameter evaluation
|
||||
&& !inDeadModule && !inParameterizedTemplate && !inTypeTable) {
|
||||
nodep->v3warn(ASCRANGE, "Ascending bit range vector: left < right of bit range: ["
|
||||
<< nodep->leftConst() << ":" << nodep->rightConst()
|
||||
<< "]");
|
||||
|
|
@ -1035,6 +1052,11 @@ class WidthVisitor final : public VNVisitor {
|
|||
}
|
||||
UASSERT_OBJ(nodep->dtypep(), nodep, "dtype wasn't set"); // by V3WidthSel
|
||||
|
||||
// Suppress SELRANGE in parameterized template modules where
|
||||
// parameter-dependent widths haven't been resolved yet.
|
||||
const bool inParameterizedTemplate
|
||||
= m_modep && (m_modep->dead() || m_modep->parameterizedTemplate());
|
||||
|
||||
if (VN_IS(nodep->lsbp(), Const) && nodep->msbConst() < nodep->lsbConst()) {
|
||||
// Likely impossible given above width check
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
|
|
@ -1047,7 +1069,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
nodep->lsbp()->replaceWith(new AstConst{nodep->lsbp()->fileline(), 0});
|
||||
}
|
||||
// We're extracting, so just make sure the expression is at least wide enough.
|
||||
if (nodep->fromp()->width() < width) {
|
||||
if (nodep->fromp()->width() < width && !inParameterizedTemplate) {
|
||||
nodep->v3warn(SELRANGE, "Extracting " << width << " bits from only "
|
||||
<< nodep->fromp()->width() << " bit number");
|
||||
// Extend it.
|
||||
|
|
@ -1102,7 +1124,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
AstNodeVarRef* lrefp = AstNodeVarRef::varRefLValueRecurse(nodep);
|
||||
if (m_doGenerate) {
|
||||
UINFO(5, "Selection index out of range inside generate");
|
||||
} else {
|
||||
} else if (!inParameterizedTemplate) {
|
||||
nodep->v3warn(SELRANGE, "Selection index out of range: "
|
||||
<< nodep->msbConst() << ":" << nodep->lsbConst()
|
||||
<< " outside " << frommsb << ":" << fromlsb);
|
||||
|
|
@ -1196,11 +1218,13 @@ class WidthVisitor final : public VNVisitor {
|
|||
if (VN_IS(nodep->bitp(), Const)
|
||||
&& (VN_AS(nodep->bitp(), Const)->toSInt() > (frommsb - fromlsb)
|
||||
|| VN_AS(nodep->bitp(), Const)->toSInt() < 0)) {
|
||||
nodep->v3warn(SELRANGE,
|
||||
"Selection index out of range: "
|
||||
<< (VN_AS(nodep->bitp(), Const)->toSInt() + fromlsb)
|
||||
<< " outside " << frommsb << ":" << fromlsb);
|
||||
UINFO(1, " Related node: " << nodep);
|
||||
// Suppress in dead/parameterized template modules
|
||||
if (!(m_modep && (m_modep->dead() || m_modep->parameterizedTemplate()))) {
|
||||
nodep->v3warn(SELRANGE,
|
||||
"Selection index out of range: "
|
||||
<< (VN_AS(nodep->bitp(), Const)->toSInt() + fromlsb)
|
||||
<< " outside " << frommsb << ":" << fromlsb);
|
||||
}
|
||||
}
|
||||
widthCheckSized(nodep, "Extract Range", nodep->bitp(), selwidthDTypep, EXTEND_EXP,
|
||||
false /*NOWARN*/);
|
||||
|
|
@ -1842,8 +1866,9 @@ class WidthVisitor final : public VNVisitor {
|
|||
switch (nodep->attrType()) {
|
||||
case VAttrType::DIM_DIMENSIONS:
|
||||
case VAttrType::DIM_UNPK_DIMENSIONS: {
|
||||
UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression");
|
||||
const std::pair<uint32_t, uint32_t> dim = nodep->fromp()->dtypep()->dimensions(true);
|
||||
AstNodeDType* const dtypep = fromDTypep(nodep->fromp());
|
||||
UASSERT_OBJ(dtypep, nodep, "Unsized expression");
|
||||
const std::pair<uint32_t, uint32_t> dim = dtypep->dimensions(true);
|
||||
const int val
|
||||
= (nodep->attrType() == VAttrType::DIM_UNPK_DIMENSIONS ? dim.second
|
||||
: (dim.first + dim.second));
|
||||
|
|
@ -1872,8 +1897,8 @@ class WidthVisitor final : public VNVisitor {
|
|||
case VAttrType::DIM_LOW:
|
||||
case VAttrType::DIM_RIGHT:
|
||||
case VAttrType::DIM_SIZE: {
|
||||
UASSERT_OBJ(nodep->fromp() && nodep->fromp()->dtypep(), nodep, "Unsized expression");
|
||||
AstNodeDType* const dtypep = nodep->fromp()->dtypep();
|
||||
AstNodeDType* const dtypep = fromDTypep(nodep->fromp());
|
||||
UASSERT_OBJ(dtypep, nodep, "Unsized expression");
|
||||
if (VN_IS(dtypep, QueueDType) || VN_IS(dtypep, DynArrayDType)) {
|
||||
switch (nodep->attrType()) {
|
||||
case VAttrType::DIM_SIZE: {
|
||||
|
|
@ -1940,9 +1965,11 @@ class WidthVisitor final : public VNVisitor {
|
|||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
} else {
|
||||
AstNodeDType* const baseDTypep = dtypep->skipRefp();
|
||||
UASSERT_OBJ(baseDTypep, nodep, "Unsized expression");
|
||||
const int dim = 1;
|
||||
AstConst* const newp
|
||||
= dimensionValue(nodep->fileline(), dtypep, nodep->attrType(), dim);
|
||||
AstConst* const newp = dimensionValue(nodep->fileline(), baseDTypep,
|
||||
nodep->attrType(), dim);
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
|
|
@ -2033,6 +2060,34 @@ class WidthVisitor final : public VNVisitor {
|
|||
// Only used in CStmts which don't care....
|
||||
}
|
||||
|
||||
void visit(AstCellArrayRef* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return;
|
||||
userIterateAndNext(nodep->selp(), WidthVP{SELF, PRELIM}.p());
|
||||
userIterateAndNext(nodep->selp(), WidthVP{SELF, FINAL}.p());
|
||||
nodep->dtypeSetVoid(); // placeholder; this node shouldnt survive beyond linking
|
||||
nodep->didWidth(true);
|
||||
}
|
||||
|
||||
void visit(AstCellRef* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return;
|
||||
|
||||
if (AstNodeExpr* const cellExprp = VN_CAST(nodep->cellp(), NodeExpr)) {
|
||||
userIterateAndNext(cellExprp, WidthVP{SELF, PRELIM}.p());
|
||||
userIterateAndNext(cellExprp, WidthVP{SELF, FINAL}.p());
|
||||
}
|
||||
|
||||
if (AstNodeExpr* const exprp = VN_CAST(nodep->exprp(), NodeExpr)) {
|
||||
userIterateAndNext(exprp, WidthVP{SELF, PRELIM}.p());
|
||||
nodep->dtypeFrom(exprp);
|
||||
userIterateAndNext(exprp, WidthVP{SELF, FINAL}.p());
|
||||
} else {
|
||||
nodep->v3fatalSrc("CellRef exprp is not a NodeExpr: " // LCOV_EXCL_LINE
|
||||
<< nodep->exprp()->prettyTypeName());
|
||||
}
|
||||
|
||||
nodep->didWidth(true);
|
||||
}
|
||||
|
||||
// DTYPES
|
||||
void visit(AstNodeArrayDType* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||
|
|
@ -2226,6 +2281,16 @@ class WidthVisitor final : public VNVisitor {
|
|||
}
|
||||
// Effectively nodep->dtypeFrom(nodep->dtypeSkipRefp());
|
||||
// But might be recursive, so instead manually recurse into the referenced type
|
||||
if (!nodep->subDTypep()) {
|
||||
// Defer unlinked RefDTypes in parameterized template modules (or types
|
||||
// with no owning module, e.g. moved to TypeTable) until specialization
|
||||
// resolves them.
|
||||
const bool inTemplateModule = !m_modep || m_modep->parameterizedTemplate();
|
||||
if (inTemplateModule) {
|
||||
nodep->doingWidth(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
UASSERT_OBJ(nodep->subDTypep(), nodep, "Unlinked");
|
||||
nodep->dtypeFrom(nodep->subDTypep());
|
||||
nodep->widthFromSub(nodep->subDTypep());
|
||||
|
|
@ -2251,9 +2316,13 @@ class WidthVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstParamTypeDType* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||
|
||||
nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
|
||||
userIterateChildren(nodep, nullptr);
|
||||
nodep->widthFromSub(nodep->subDTypep());
|
||||
// Clear childDTypep after dtypep is set to satisfy V3Broken invariant.
|
||||
// The child dtype has been moved to the type table by iterateEditMoveDTypep.
|
||||
if (nodep->dtypep() && nodep->childDTypep()) { nodep->childDTypep(nullptr); }
|
||||
}
|
||||
void visit(AstRequireDType* nodep) override {
|
||||
userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
|
||||
|
|
@ -2631,6 +2700,10 @@ class WidthVisitor final : public VNVisitor {
|
|||
const bool implicitParam = nodep->isParam() && bdtypep && bdtypep->implicit();
|
||||
if (implicitParam) {
|
||||
if (nodep->valuep()) {
|
||||
// Remove blanket deferral. We must attempt to visit the value to determine
|
||||
// type/deps. If it remains unresolved, specific node visitors (like AttrOf) should
|
||||
// handle deferral by setting a placeholder type to prevent "No dtype" errors
|
||||
// later.
|
||||
userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p());
|
||||
UINFO(9, "implicitParamPRELIMIV " << nodep->valuep());
|
||||
// Although nodep will get a different width for parameters
|
||||
|
|
@ -2645,7 +2718,10 @@ class WidthVisitor final : public VNVisitor {
|
|||
VL_DANGLING(bdtypep);
|
||||
} else {
|
||||
int width = 0;
|
||||
const AstBasicDType* const valueBdtypep = nodep->valuep()->dtypep()->basicp();
|
||||
AstNodeDType* const valueDTypep = nodep->valuep()->dtypep();
|
||||
UASSERT_OBJ(valueDTypep, nodep->valuep(),
|
||||
"Null dtype on implicit param value");
|
||||
const AstBasicDType* const valueBdtypep = valueDTypep->basicp();
|
||||
bool issigned = false;
|
||||
if (bdtypep->isNosign()) {
|
||||
if (valueBdtypep && valueBdtypep->isSigned()) issigned = true;
|
||||
|
|
@ -3278,6 +3354,14 @@ class WidthVisitor final : public VNVisitor {
|
|||
const bool isHardPackedUnion
|
||||
= nodep->packed() && VN_IS(nodep, UnionDType) && !VN_CAST(nodep, UnionDType)->isSoft();
|
||||
|
||||
// Suppress union size errors in parameterized template modules where member
|
||||
// widths depend on unresolved parameters. Also suppress when the type has no
|
||||
// owning module (e.g. moved to TypeTable during DepGraph resolution).
|
||||
// TODO: Revisit this gate if DepGraph becomes the sole flow and widthing
|
||||
// can assume all types are already specialized.
|
||||
const bool inTemplateModule = (m_modep && m_modep->parameterizedTemplate())
|
||||
|| (VN_IS(nodep, UnionDType) && !m_modep);
|
||||
|
||||
// Determine bit assignments and width
|
||||
if (VN_IS(nodep, UnionDType) || nodep->packed()) {
|
||||
int lsb = 0;
|
||||
|
|
@ -3293,7 +3377,8 @@ class WidthVisitor final : public VNVisitor {
|
|||
itemp->lsb(lsb);
|
||||
if (VN_IS(nodep, UnionDType)) {
|
||||
const int itemWidth = itemp->width();
|
||||
if (!first && isHardPackedUnion && itemWidth != width) {
|
||||
// Skip union size check for template modules with unresolved parameters
|
||||
if (!first && isHardPackedUnion && itemWidth != width && !inTemplateModule) {
|
||||
itemp->v3error("Hard packed union members must have equal size "
|
||||
"(IEEE 1800-2023 7.3.1)");
|
||||
}
|
||||
|
|
@ -7899,6 +7984,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
"Under node " << nodep->prettyTypeName()
|
||||
<< " has no dtype?? Missing Visitor func?");
|
||||
if (expDTypep->basicp()->untyped() || nodep->dtypep()->basicp()->untyped()) return false;
|
||||
// During DepGraph execution, expected width may be 0 if the type hasn't been
|
||||
UASSERT_OBJ(nodep->width() != 0, nodep,
|
||||
"Under node " << nodep->prettyTypeName()
|
||||
<< " has no expected width?? Missing Visitor func?");
|
||||
|
|
@ -8871,6 +8957,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
UASSERT_OBJ(dtnodep->didWidth(), parentp,
|
||||
"iterateEditMoveDTypep didn't get width resolution of "
|
||||
<< dtnodep->prettyTypeName());
|
||||
|
||||
// Move to under netlist
|
||||
UINFO(9, "iterateEditMoveDTypep child moving " << dtnodep);
|
||||
dtnodep->unlinkFrBack();
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@
|
|||
#include "V3LifePost.h"
|
||||
#include "V3LiftExpr.h"
|
||||
#include "V3LinkDot.h"
|
||||
#include "V3LinkDotIfaceCapture.h"
|
||||
#include "V3LinkInc.h"
|
||||
#include "V3LinkJump.h"
|
||||
#include "V3LinkLValue.h"
|
||||
|
|
@ -178,10 +179,15 @@ static void process() {
|
|||
// This requires some width calculations and constant propagation
|
||||
// No more AstGenCase/AstGenFor/AstGenIf after this
|
||||
V3Param::param(v3Global.rootp());
|
||||
|
||||
V3LinkDot::linkDotParamed(v3Global.rootp()); // Cleanup as made new modules
|
||||
V3LinkLValue::linkLValue(v3Global.rootp()); // Resolve new VarRefs
|
||||
V3Error::abortIfErrors();
|
||||
|
||||
// Fix any remaining cross-interface refs created during V3Width::widthParamsEdit
|
||||
// that weren't captured earlier. Must run before V3Dead deletes template modules.
|
||||
V3LinkDotIfaceCapture::finalizeIfaceCapture();
|
||||
|
||||
// Remove any modules that were parameterized and are no longer referenced.
|
||||
V3Dead::deadifyModules(v3Global.rootp());
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
class Class1 #(
|
||||
type T
|
||||
);
|
||||
static function int get_p();
|
||||
return 7;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Class2 #(
|
||||
type T
|
||||
) extends Class1 #(T);
|
||||
static function int get_p2;
|
||||
return T::get_p();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
typedef Class2#(Class1#(int)) Class;
|
||||
if (Class::get_p2() != Class1#(int)::get_p()) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 by Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# Verifies that the self-referential interface typedef pattern
|
||||
# (typedef iface#(T) self_t inside interface iface) is detected
|
||||
# and handled correctly during deparameterization.
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile(v_flags2=["--binary --stats"])
|
||||
|
||||
test.file_grep(test.stats, r'Param, Self-reference iface typedefs\s+(\d+)', 1)
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// DESCRIPTION: Verilator: Test for self-referential interface typedef
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// Self-referential typedef: typedef iface#(T) this_type inside interface iface
|
||||
interface my_iface #(type T = logic);
|
||||
typedef my_iface #(T) self_t;
|
||||
T data;
|
||||
endinterface
|
||||
|
||||
module t ();
|
||||
my_iface #(logic [7:0]) if0 ();
|
||||
|
||||
initial begin
|
||||
if0.data = 8'hAB;
|
||||
if ($bits(if0.data) != 8) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Stress test for interface typedef scaling. Generates an interface with
|
||||
# many typedefs and two parameterised instances to exercise the
|
||||
# findTypedefInModule / findDTypeInModule caching path.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import time
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.top_filename = test.obj_dir + "/t_iface_typedef_scale.sv"
|
||||
|
||||
N_TYPEDEFS = 5000
|
||||
MAX_COMPILE_SECS = 10 # Generous budget; catches O(N^2) explosion where 5k typedefs would take minutes without the cache
|
||||
|
||||
|
||||
def gen(filename, n):
|
||||
with open(filename, 'w', encoding="utf8") as fh:
|
||||
fh.write("// Generated by t_iface_typedef_scale.py\n")
|
||||
fh.write("// Stress test: interface with {} typedefs, two instances\n".format(n))
|
||||
fh.write("\n")
|
||||
# Interface with N typedefs
|
||||
fh.write("interface iface_many_types #(parameter int CFG = 8);\n")
|
||||
for i in range(n):
|
||||
fh.write(" typedef logic [CFG-1:0] td_{};\n".format(i))
|
||||
fh.write("endinterface\n\n")
|
||||
# Module that uses the interface and references typedefs via the port
|
||||
fh.write("module sub (\n")
|
||||
fh.write(" iface_many_types ifc,\n")
|
||||
fh.write(" input logic clk\n")
|
||||
fh.write(");\n")
|
||||
fh.write(" typedef ifc.td_0 local_td_0;\n")
|
||||
fh.write(" typedef ifc.td_{} local_td_n;\n".format(n - 1))
|
||||
fh.write(" local_td_0 r0;\n")
|
||||
fh.write(" local_td_n rn;\n")
|
||||
fh.write(" always @(posedge clk) begin\n")
|
||||
fh.write(" r0 <= '0;\n")
|
||||
fh.write(" rn <= '0;\n")
|
||||
fh.write(" end\n")
|
||||
fh.write("endmodule\n\n")
|
||||
# Top module with two instances using different parameters
|
||||
fh.write("module t (input logic clk);\n")
|
||||
fh.write(" iface_many_types #(.CFG(16)) ifc_a();\n")
|
||||
fh.write(" iface_many_types #(.CFG(32)) ifc_b();\n")
|
||||
fh.write(" sub sub_a (.ifc(ifc_a), .clk(clk));\n")
|
||||
fh.write(" sub sub_b (.ifc(ifc_b), .clk(clk));\n")
|
||||
fh.write(" initial begin\n")
|
||||
fh.write(' $write("*-* All Finished *-*\\n");\n')
|
||||
fh.write(" $finish;\n")
|
||||
fh.write(" end\n")
|
||||
fh.write("endmodule\n")
|
||||
|
||||
|
||||
gen(test.top_filename, N_TYPEDEFS)
|
||||
|
||||
test.timeout(MAX_COMPILE_SECS)
|
||||
|
||||
t0 = time.time()
|
||||
test.compile(verilator_flags2=["-x-assign fast --x-initial fast"])
|
||||
elapsed = time.time() - t0
|
||||
|
||||
print("t_iface_typedef_scale: {} typedefs compiled in {:.3f}s (limit {:.1f}s)".format(
|
||||
N_TYPEDEFS, elapsed, MAX_COMPILE_SECS))
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Test for MemberDType fixup in type table
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.top_filename = "t/t_iface_typedef_struct_member.v"
|
||||
|
||||
test.compile(v_flags2=["--binary"])
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// DESCRIPTION: Verilator: Test for MemberDType fixup and fixDeadRefs in type table (coverage)
|
||||
//
|
||||
// Parameterized interface with struct/union typedefs, instantiated in two
|
||||
// modules. The sub-module uses $bits() on a typedef, which forces widthing
|
||||
// of the template's type chain during V3Param. This moves template RefDTypes
|
||||
// into the global type table before the template dies, exercising
|
||||
// fixDeadRefsInTypeTable and the MemberDType fixup in V3LinkDotIfaceCapture.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv, expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package cfg_pkg;
|
||||
typedef struct packed {
|
||||
int unsigned NumUnits;
|
||||
int unsigned LineSize;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
// Parameterized interface with nested struct/union typedefs.
|
||||
// The struct-in-union pattern triggers MemberDType fixup (line 1056).
|
||||
// The $bits() usage in sub_mod forces widthing during V3Param, moving
|
||||
// template RefDTypes into the type table before the template dies.
|
||||
interface types_if #(parameter cfg_pkg::cfg_t cfg = 0)();
|
||||
typedef logic [$clog2(cfg.NumUnits)-1:0] idx_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic [31:$clog2(cfg.LineSize)] tag;
|
||||
logic [$clog2(cfg.LineSize)-1:0] offset;
|
||||
} addr_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic [3:0] cmd;
|
||||
union packed {
|
||||
addr_t addr; // struct-in-union: MemberDType trigger
|
||||
logic [31:0] raw;
|
||||
} payload;
|
||||
} req_t;
|
||||
endinterface
|
||||
|
||||
module sub_mod #(parameter cfg_pkg::cfg_t cfg = 0)();
|
||||
types_if #(cfg) types();
|
||||
typedef types.idx_t idx_t;
|
||||
typedef types.req_t req_t;
|
||||
typedef types.addr_t addr_t;
|
||||
|
||||
localparam int ReqWidth = $bits(req_t);
|
||||
|
||||
idx_t s_idx;
|
||||
req_t s_req;
|
||||
addr_t s_addr;
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
localparam cfg_pkg::cfg_t CFG = '{NumUnits: 5, LineSize: 32};
|
||||
|
||||
types_if #(CFG) types();
|
||||
typedef types.req_t req_t;
|
||||
req_t top_req;
|
||||
|
||||
sub_mod #(.cfg(CFG)) sub();
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd($bits(top_req), 36);
|
||||
`checkd($bits(sub.s_req), 36);
|
||||
`checkd($bits(sub.s_idx), 3);
|
||||
`checkd($bits(sub.s_addr), 32);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile(v_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// DESCRIPTION: Verilator: Test for structural disambiguation and wrong-clone
|
||||
// fixup in V3LinkDotIfaceCapture.
|
||||
//
|
||||
// Modeled after Aerial's simple_cache_if pattern:
|
||||
// - A parameterized interface (sc_if) contains a nested sub-interface (sc_io)
|
||||
// - The sub-interface has typedefs (addr_t)
|
||||
// - A wrapper module (sc_wrap) instantiates sc_if and uses its typedefs
|
||||
// - Two different parameterizations of sc_wrap exist
|
||||
// - The re-exported typedefs from sc_io create entries where followCellPath
|
||||
// may fail, triggering the structural disambiguation path
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
typedef struct packed {
|
||||
int unsigned AddrBits;
|
||||
int unsigned DataBits;
|
||||
} sc_cfg_t;
|
||||
|
||||
// Inner types interface - parameterized with struct typedef
|
||||
interface sc_types_if #(parameter sc_cfg_t cfg = 0)();
|
||||
typedef logic [cfg.AddrBits-1:0] addr_t;
|
||||
typedef logic [cfg.DataBits-1:0] data_t;
|
||||
|
||||
typedef struct packed {
|
||||
addr_t addr;
|
||||
data_t data;
|
||||
} pkt_t;
|
||||
endinterface
|
||||
|
||||
// Cache interface - wraps types interface and re-exports typedefs
|
||||
interface sc_if #(parameter sc_cfg_t cfg = 0)();
|
||||
sc_types_if #(cfg) sc_io();
|
||||
|
||||
typedef sc_io.addr_t addr_t;
|
||||
typedef sc_io.data_t data_t;
|
||||
typedef sc_io.pkt_t pkt_t;
|
||||
|
||||
addr_t rq_addr_i;
|
||||
endinterface
|
||||
|
||||
// Wrapper module that uses the cache interface
|
||||
module sc_wrap #(parameter sc_cfg_t cfg = 0)();
|
||||
sc_if #(cfg) cache();
|
||||
|
||||
typedef cache.addr_t addr_t;
|
||||
typedef cache.pkt_t pkt_t;
|
||||
|
||||
addr_t local_addr;
|
||||
pkt_t local_pkt;
|
||||
|
||||
assign cache.rq_addr_i = local_addr;
|
||||
endmodule
|
||||
|
||||
// Top-level: two wrappers with DIFFERENT configs
|
||||
// This creates two clones of sc_if (and sc_types_if) with different params
|
||||
module t;
|
||||
localparam sc_cfg_t cfg_narrow = '{AddrBits: 16, DataBits: 32};
|
||||
localparam sc_cfg_t cfg_wide = '{AddrBits: 32, DataBits: 64};
|
||||
|
||||
sc_wrap #(.cfg(cfg_narrow)) narrow();
|
||||
sc_wrap #(.cfg(cfg_wide)) wide();
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
// narrow: addr=16, data=32, pkt=16+32=48
|
||||
`checkd($bits(narrow.local_addr), 16);
|
||||
`checkd($bits(narrow.local_pkt), 48);
|
||||
|
||||
// wide: addr=32, data=64, pkt=32+64=96
|
||||
`checkd($bits(wide.local_addr), 32);
|
||||
`checkd($bits(wide.local_pkt), 96);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -11,8 +11,8 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator_st')
|
||||
|
||||
test.compile(fails=True)
|
||||
test.compile(verilator_flags2=['--binary'])
|
||||
|
||||
#test.execute(fails=True)
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -1,22 +1,10 @@
|
|||
%Error: t/t_interface_nested_port_array.v:160:5: Interface 'l3_if' not connected as parent's interface not connected
|
||||
: ... note: In instance 't.m_l3'
|
||||
: ... Perhaps caused by another error on the parent interface that needs resolving
|
||||
: ... Or, perhaps intended an interface instantiation but are missing parenthesis (IEEE 1800-2023 25.3)?
|
||||
160 | l3_if#(W, L0A_W) l3,
|
||||
| ^~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_interface_nested_port_array.v:123:5: Interface 'l2_if' not connected as parent's interface not connected
|
||||
: ... note: In instance 't.m_l3.m_l2'
|
||||
: ... Perhaps caused by another error on the parent interface that needs resolving
|
||||
: ... Or, perhaps intended an interface instantiation but are missing parenthesis (IEEE 1800-2023 25.3)?
|
||||
123 | l2_if#(W, L0A_W) l2s[1:0],
|
||||
| ^~~~~
|
||||
%Error: t/t_interface_nested_port_array.v:91:5: Interface 'l2_if' not connected as parent's interface not connected
|
||||
: ... note: In instance 't.m_l3.m_l2.m_l2b'
|
||||
: ... Perhaps caused by another error on the parent interface that needs resolving
|
||||
: ... Or, perhaps intended an interface instantiation but are missing parenthesis (IEEE 1800-2023 25.3)?
|
||||
91 | l2_if#(W, L0A_W) l2,
|
||||
| ^~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Internal Error: t/t_interface_nested_port_array.v:27:11: ../V3LinkDot.cpp:#: Module/etc never assigned a symbol entry?
|
||||
: ... note: In instance 't.m_l3.m_l2.m_l2b.m_l1_1'
|
||||
27 | interface l2_if #(
|
||||
|
|
|
|||
|
|
@ -1,22 +1,10 @@
|
|||
%Error: t/t_interface_nested_port_array.v:160:5: Interface 'l3_if' not connected as parent's interface not connected
|
||||
: ... note: In instance 't.m_l3'
|
||||
: ... Perhaps caused by another error on the parent interface that needs resolving
|
||||
: ... Or, perhaps intended an interface instantiation but are missing parenthesis (IEEE 1800-2023 25.3)?
|
||||
160 | l3_if#(W, L0A_W) l3,
|
||||
| ^~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_interface_nested_port_array.v:123:5: Interface 'l2_if' not connected as parent's interface not connected
|
||||
: ... note: In instance 't.m_l3.m_l2'
|
||||
: ... Perhaps caused by another error on the parent interface that needs resolving
|
||||
: ... Or, perhaps intended an interface instantiation but are missing parenthesis (IEEE 1800-2023 25.3)?
|
||||
123 | l2_if#(W, L0A_W) l2s[1:0],
|
||||
| ^~~~~
|
||||
%Error: t/t_interface_nested_port_array.v:91:5: Interface 'l2_if' not connected as parent's interface not connected
|
||||
: ... note: In instance 't.m_l3.m_l2.m_l2b'
|
||||
: ... Perhaps caused by another error on the parent interface that needs resolving
|
||||
: ... Or, perhaps intended an interface instantiation but are missing parenthesis (IEEE 1800-2023 25.3)?
|
||||
91 | l2_if#(W, L0A_W) l2,
|
||||
| ^~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Internal Error: t/t_interface_nested_port_array.v:27:11: ../V3LinkDot.cpp:#: Module/etc never assigned a symbol entry?
|
||||
: ... note: In instance 't.m_l3.m_l2.m_l2b.m_l1_1'
|
||||
27 | interface l2_if #(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['--binary'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
|
||||
// Test: nested parameterized interface with struct typedef used as type parameter
|
||||
//
|
||||
// Reproduces a bug where specializeNestedIfaceCells causes early
|
||||
// specialization of a nested interface, leaving PARAMTYPEDTYPE child
|
||||
// REFDTYPEs pointing to the template struct instead of the clone struct.
|
||||
// The struct's width then resolves using the template's default parameter
|
||||
// values instead of the actual overridden values.
|
||||
//
|
||||
// Pattern (mirrors Aerial's simple_cache / simple_cache_if / simple_cache_types_if):
|
||||
// 1. A wrapper interface instantiates a nested types interface
|
||||
// 2. The types interface computes localparams from cfg (using $clog2/division)
|
||||
// 3. Those localparams define typedef ranges for struct members
|
||||
// 4. A module receives the wrapper interface as a port, also instantiates
|
||||
// the types interface locally, and uses the struct typedef
|
||||
// 5. Both the wrapper and the module use the same cfg, so they share
|
||||
// the same types_if clone via "De-parameterize to prev"
|
||||
|
||||
package cfg_pkg;
|
||||
typedef struct packed {
|
||||
logic [31:0] AddrBits;
|
||||
logic [31:0] Capacity;
|
||||
logic [31:0] LineSize;
|
||||
logic [31:0] Associativity;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
// Nested types interface: derives struct typedef from computed localparams
|
||||
interface types_if #(
|
||||
parameter cfg_pkg::cfg_t cfg = '0
|
||||
)();
|
||||
// Computed localparams - these use division and $clog2 of cfg fields.
|
||||
// With default cfg='0, these produce X/undefined values.
|
||||
localparam int NUM_LINES = cfg.Capacity / cfg.LineSize;
|
||||
localparam int LINES_PER_WAY = NUM_LINES / cfg.Associativity;
|
||||
localparam int BLOCK_BITS = $clog2(cfg.LineSize);
|
||||
localparam int ROW_BITS = $clog2(LINES_PER_WAY);
|
||||
localparam int TAG_BITS = cfg.AddrBits - ROW_BITS - BLOCK_BITS;
|
||||
|
||||
typedef logic [TAG_BITS-1:0] tag_t;
|
||||
typedef logic [ROW_BITS-1:0] row_t;
|
||||
typedef logic [BLOCK_BITS-1:0] block_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic vld;
|
||||
tag_t tag;
|
||||
row_t row;
|
||||
block_t block;
|
||||
} entry_t;
|
||||
endinterface
|
||||
|
||||
// Wrapper interface: instantiates types_if as a nested cell
|
||||
// (mirrors simple_cache_if which instantiates simple_cache_types_if)
|
||||
interface wrapper_if #(
|
||||
parameter cfg_pkg::cfg_t cfg = '0
|
||||
)();
|
||||
types_if #(cfg) types();
|
||||
|
||||
typedef types.tag_t tag_t;
|
||||
|
||||
logic req_vld;
|
||||
tag_t req_tag;
|
||||
endinterface
|
||||
|
||||
// Sub-module parameterized by entry width
|
||||
// (mirrors flop_nr / sram_generic_1r1w parameterized by $bits(sc_tag_t))
|
||||
module entry_store #(
|
||||
parameter int ENTRY_WIDTH = 8,
|
||||
parameter int DEPTH = 4
|
||||
)(
|
||||
input logic clk,
|
||||
input logic wr_en,
|
||||
input logic [ENTRY_WIDTH-1:0] wr_data,
|
||||
output logic [ENTRY_WIDTH-1:0] rd_data
|
||||
);
|
||||
logic [ENTRY_WIDTH-1:0] mem [DEPTH];
|
||||
always_ff @(posedge clk) begin
|
||||
if (wr_en) mem[0] <= wr_data;
|
||||
end
|
||||
assign rd_data = mem[0];
|
||||
endmodule
|
||||
|
||||
// Inner module: receives wrapper_if as port, instantiates types_if locally,
|
||||
// uses struct typedef from types_if
|
||||
// (mirrors simple_cache which receives simple_cache_if, instantiates
|
||||
// simple_cache_types_if, and uses types.sc_tag_t)
|
||||
module inner_mod #(
|
||||
parameter cfg_pkg::cfg_t cfg = '0
|
||||
)(
|
||||
input logic clk,
|
||||
wrapper_if io
|
||||
);
|
||||
// Local instantiation of types_if - same cfg, so gets same clone
|
||||
// as the one inside wrapper_if via "De-parameterize to prev"
|
||||
types_if #(cfg) types();
|
||||
|
||||
typedef types.entry_t entry_t;
|
||||
typedef types.tag_t tag_t;
|
||||
|
||||
entry_t wr_entry;
|
||||
entry_t rd_entry;
|
||||
|
||||
assign wr_entry.vld = io.req_vld;
|
||||
assign wr_entry.tag = io.req_tag;
|
||||
assign wr_entry.row = '0;
|
||||
assign wr_entry.block = '0;
|
||||
|
||||
// Use $bits of the struct typedef as a value parameter to sub-module.
|
||||
// This is the critical pattern: $bits(entry_t) must resolve using the
|
||||
// clone's struct (correct width), not the template's (zero/X width).
|
||||
entry_store #(
|
||||
.ENTRY_WIDTH($bits(entry_t)),
|
||||
.DEPTH(8)
|
||||
) u_store (
|
||||
.clk(clk),
|
||||
.wr_en(io.req_vld),
|
||||
.wr_data(wr_entry),
|
||||
.rd_data(rd_entry)
|
||||
);
|
||||
endmodule
|
||||
|
||||
// Outer wrapper module: instantiates wrapper_if and inner_mod
|
||||
// (mirrors mblit_simple_cache_wrap)
|
||||
module outer_mod #(
|
||||
parameter cfg_pkg::cfg_t cfg = '0
|
||||
)(
|
||||
input logic clk
|
||||
);
|
||||
wrapper_if #(cfg) wif();
|
||||
|
||||
inner_mod #(cfg) u_inner (
|
||||
.clk(clk),
|
||||
.io(wif)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
logic clk = 0;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
int cyc = 0;
|
||||
|
||||
// Non-default config:
|
||||
// AddrBits=64, Capacity=1024, LineSize=64, Associativity=2
|
||||
// NUM_LINES = 1024/64 = 16
|
||||
// LINES_PER_WAY = 16/2 = 8
|
||||
// BLOCK_BITS = $clog2(64) = 6
|
||||
// ROW_BITS = $clog2(8) = 3
|
||||
// TAG_BITS = 64 - 3 - 6 = 55
|
||||
// entry_t = 1 + 55 + 3 + 6 = 65 bits
|
||||
localparam cfg_pkg::cfg_t MY_CFG = '{
|
||||
AddrBits: 64,
|
||||
Capacity: 1024,
|
||||
LineSize: 64,
|
||||
Associativity: 2
|
||||
};
|
||||
|
||||
outer_mod #(.cfg(MY_CFG)) u_outer (.clk(clk));
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
|
||||
u_outer.wif.req_vld <= (cyc[0] == 1'b1);
|
||||
u_outer.wif.req_tag <= 55'(cyc);
|
||||
|
||||
if (cyc > 5) begin
|
||||
// Verify the struct round-trips correctly
|
||||
if (u_outer.u_inner.rd_entry.vld !== 1'b1 && cyc > 10) begin
|
||||
$display("FAIL cyc=%0d: rd_entry.vld=%b expected 1",
|
||||
cyc, u_outer.u_inner.rd_entry.vld);
|
||||
$stop;
|
||||
end
|
||||
end
|
||||
|
||||
if (cyc == 20) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
typedef struct {
|
||||
int BAR_INT;
|
||||
bit BAR_BIT;
|
||||
byte BAR_ARRAY [0:3];
|
||||
} foo_t;
|
||||
|
||||
interface intf
|
||||
#(parameter foo_t FOO = '{4, 1'b1, '{8'd1, 8'd2, 8'd4, 8'd8}})
|
||||
();
|
||||
endinterface
|
||||
|
||||
module sub (intf the_intf_port [4]);
|
||||
localparam int intf_foo_bar_int = the_intf_port[0].FOO.BAR_INT;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
if (intf_foo_bar_int != 4) $stop;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t ();
|
||||
intf the_intf [4] ();
|
||||
|
||||
sub
|
||||
the_sub (
|
||||
.the_intf_port (the_intf)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -31,8 +31,8 @@ module top();
|
|||
.p_dwidth(8)
|
||||
) if0();
|
||||
|
||||
localparam p0_rq_t = if0.rq_t;
|
||||
localparam p0_rs_t = if0.rs_t;
|
||||
localparam type p0_rq_t = if0.rq_t;
|
||||
localparam type p0_rs_t = if0.rs_t;
|
||||
|
||||
p0_rq_t rq;
|
||||
p0_rs_t rs;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
%Error: t/t_lparam_assign_iface_typedef_bad.v:28:3: Expecting a data type: 'p0_rq_t'
|
||||
28 | p0_rq_t rq;
|
||||
| ^~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2025 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
//
|
||||
// localparam without 'type' keyword should error, not silently ignore.
|
||||
// Correct syntax is: localparam type p0_rq_t = if0.rq_t;
|
||||
|
||||
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;
|
||||
endinterface
|
||||
|
||||
module t();
|
||||
x_if #(
|
||||
.p_awidth(16),
|
||||
.p_dwidth(8)
|
||||
) if0();
|
||||
|
||||
localparam p0_rq_t = if0.rq_t; // Bad: missing 'type' keyword
|
||||
|
||||
p0_rq_t rq; // Should fail: p0_rq_t is not a data type
|
||||
endmodule
|
||||
|
|
@ -37,7 +37,7 @@ module top();
|
|||
.p_dwidth(8)
|
||||
) if0();
|
||||
|
||||
localparam p0_rq2_t = if0.y_if0.rq2_t;
|
||||
localparam type p0_rq2_t = if0.y_if0.rq2_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ module top ();
|
|||
.p_dwidth(8)
|
||||
) if0 ();
|
||||
|
||||
localparam p0_rq2_t = if0.y_if0.rq2_t;
|
||||
localparam type p0_rq2_t = if0.y_if0.rq2_t;
|
||||
|
||||
p0_rq2_t p0_rq2;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ module top ();
|
|||
.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 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;
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@ module top ();
|
|||
.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;
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ endinterface
|
|||
module a_mod(
|
||||
bus_if bus_io
|
||||
);
|
||||
localparam bus_rq_t = bus_io.rq_t;
|
||||
localparam type bus_rq_t = bus_io.rq_t;
|
||||
endmodule
|
||||
|
||||
module top();
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ 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 type bus_rq_t = bus_io.rq_t;
|
||||
localparam type bus_rs_t = bus_io.rs_t;
|
||||
localparam p_awidth = bus_io.p_awidth;
|
||||
localparam p_dwidth = bus_io.p_dwidth;
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ module top ();
|
|||
.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 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;
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ module a_mod #(
|
|||
) (
|
||||
bus_if bus_io
|
||||
);
|
||||
localparam bus_rq_t = bus_io.rq_t;
|
||||
localparam bus_rs_t = bus_io.rs_t;
|
||||
localparam type bus_rq_t = bus_io.rq_t;
|
||||
localparam type bus_rs_t = bus_io.rs_t;
|
||||
|
||||
bus_rq_t rq;
|
||||
bus_rs_t rs;
|
||||
|
|
|
|||
|
|
@ -54,10 +54,10 @@ module top ();
|
|||
|
||||
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;
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// DESCRIPTION: Verilator: Get agregate type parameter from array of interfaces
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
typedef struct {
|
||||
int BAR_INT;
|
||||
bit BAR_BIT;
|
||||
byte BAR_ARRAY [0:3];
|
||||
} foo_t;
|
||||
|
||||
interface intf
|
||||
#(parameter foo_t FOO = '{4, 1'b1, '{8'd1, 8'd2, 8'd4, 8'd8}})
|
||||
();
|
||||
endinterface
|
||||
|
||||
module sub (intf single_intf_port);
|
||||
localparam byte single_foo_bar_byte = single_intf_port.FOO.BAR_ARRAY[3];
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd(single_foo_bar_byte, 8'd8);
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t ();
|
||||
intf single_intf ();
|
||||
|
||||
sub
|
||||
the_sub (
|
||||
.single_intf_port(single_intf)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// DESCRIPTION: Verilator: Get agregate type parameter from array of interfaces
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
typedef struct {
|
||||
int BAR_INT;
|
||||
bit BAR_BIT;
|
||||
byte BAR_ARRAY [0:3];
|
||||
} foo_t;
|
||||
|
||||
interface intf
|
||||
#(parameter foo_t FOO = '{4, 1'b1, '{8'd1, 8'd2, 8'd4, 8'd8}})
|
||||
();
|
||||
endinterface
|
||||
|
||||
module sub (intf the_intf_port [4]);
|
||||
localparam int intf_foo_bar_int = the_intf_port[0].FOO.BAR_INT;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd(intf_foo_bar_int, 4);
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t ();
|
||||
intf the_intf [4] ();
|
||||
|
||||
sub
|
||||
the_sub (
|
||||
.the_intf_port (the_intf)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free # This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// DESCRIPTION: Verilator: Multiple interface instances with different params
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package scp;
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface a_if #(
|
||||
parameter a_p = 0
|
||||
)();
|
||||
localparam int LP0 = a_p * 2;
|
||||
typedef logic [LP0-1:0] a_t;
|
||||
endinterface
|
||||
|
||||
interface sc_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
localparam int LP_MUL = cfg.ABits * cfg.BBits;
|
||||
localparam int LP_ADD = cfg.ABits + cfg.BBits;
|
||||
|
||||
a_if #(LP_MUL) types_mul();
|
||||
a_if #(LP_ADD) types_add();
|
||||
endinterface
|
||||
|
||||
module sc #(parameter scp::cfg_t cfg=0) (
|
||||
sc_if io
|
||||
);
|
||||
|
||||
typedef io.types_mul.a_t a_mul_t;
|
||||
typedef io.types_add.a_t a_add_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
// cfg.ABits=2, cfg.BBits=3
|
||||
// LP_MUL=6 -> a_p=6 -> LP0=12 -> a_mul_t is 12 bits
|
||||
// LP_ADD=5 -> a_p=5 -> LP0=10 -> a_add_t is 10 bits
|
||||
`checkd($bits(a_mul_t), 12);
|
||||
`checkd($bits(a_add_t), 10);
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
localparam scp::cfg_t sc_cfg = '{
|
||||
ABits : 2,
|
||||
BBits : 3
|
||||
};
|
||||
|
||||
sc_if #(sc_cfg) sc_io ();
|
||||
|
||||
sc #(sc_cfg) sc(
|
||||
.io(sc_io)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// DESCRIPTION: Verilator: Cross-interface typedef references
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package scp;
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface a_if #(
|
||||
parameter a_p = 0
|
||||
)();
|
||||
localparam int LP0 = a_p * 2;
|
||||
typedef logic [LP0-1:0] a_t;
|
||||
endinterface
|
||||
|
||||
interface b_if #(
|
||||
parameter b_p = 0
|
||||
)();
|
||||
localparam int LP0 = b_p + 3;
|
||||
typedef logic [LP0-1:0] b_t;
|
||||
endinterface
|
||||
|
||||
interface sc_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
localparam int LP0 = cfg.ABits * cfg.BBits;
|
||||
|
||||
a_if #(LP0) a_inst();
|
||||
b_if #(LP0) b_inst();
|
||||
|
||||
typedef a_inst.a_t a_t;
|
||||
typedef b_inst.b_t b_t;
|
||||
endinterface
|
||||
|
||||
module sc #(parameter scp::cfg_t cfg=0) (
|
||||
sc_if io
|
||||
);
|
||||
|
||||
typedef io.a_t a_t;
|
||||
typedef io.b_t b_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
// cfg.ABits=2, cfg.BBits=3 -> LP0=6
|
||||
// a_if: a_p=6 -> LP0=12 -> a_t is 12 bits
|
||||
// b_if: b_p=6 -> LP0=9 -> b_t is 9 bits
|
||||
`checkd(12, $bits(a_t));
|
||||
`checkd(9, $bits(b_t));
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
localparam scp::cfg_t sc_cfg = '{
|
||||
ABits : 2,
|
||||
BBits : 3
|
||||
};
|
||||
|
||||
sc_if #(sc_cfg) sc_io ();
|
||||
|
||||
sc #(sc_cfg) sc(
|
||||
.io(sc_io)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
// DESCRIPTION: Verilator: 4-level deep nested interface typedef with dependent localparams
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package scp;
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
// Level 4: innermost interface
|
||||
interface d_if #(
|
||||
parameter d_p = 0
|
||||
)();
|
||||
localparam int LP0 = d_p + 1;
|
||||
typedef logic [LP0-1:0] d_t;
|
||||
endinterface
|
||||
|
||||
// Level 3
|
||||
interface c_if #(
|
||||
parameter c_p = 0
|
||||
)();
|
||||
localparam int LP0 = c_p * 2;
|
||||
d_if #(LP0) d_inst();
|
||||
typedef d_inst.d_t c_t;
|
||||
endinterface
|
||||
|
||||
// Level 2
|
||||
interface b_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
localparam int LP0 = cfg.ABits * cfg.BBits;
|
||||
c_if #(LP0) c_inst();
|
||||
typedef c_inst.c_t b_t;
|
||||
endinterface
|
||||
|
||||
// Level 1: outermost interface
|
||||
interface a_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
b_if #(cfg) b_inst();
|
||||
typedef b_inst.b_t a_t;
|
||||
endinterface
|
||||
|
||||
module m #(parameter scp::cfg_t cfg=0) (
|
||||
a_if io
|
||||
);
|
||||
|
||||
typedef io.a_t a_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
// cfg.ABits=2, cfg.BBits=3
|
||||
// b_if: LP0 = 2*3 = 6
|
||||
// c_if: c_p=6, LP0 = 6*2 = 12
|
||||
// d_if: d_p=12, LP0 = 12+1 = 13
|
||||
// d_t is 13 bits
|
||||
`checkd($bits(a_t), 13);
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
localparam scp::cfg_t cfg = '{
|
||||
ABits : 2,
|
||||
BBits : 3
|
||||
};
|
||||
|
||||
a_if #(cfg) a_io ();
|
||||
|
||||
m #(cfg) m_inst(
|
||||
.io(a_io)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
// DESCRIPTION: Verilator: Get agregate type parameter from array of interfaces
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package sc;
|
||||
typedef struct packed {
|
||||
int unsigned CmdTagBits;
|
||||
int unsigned Associativity;
|
||||
int unsigned Capacity;
|
||||
int unsigned LineSize;
|
||||
int unsigned StateBits;
|
||||
int unsigned AddrBits;
|
||||
int unsigned MissQSize;
|
||||
|
||||
// fetch (hit) width. this must be >= to refill width. FgWidth / RefillWidth is the number of array slices for data.
|
||||
int unsigned FgWidth;
|
||||
// number of expected beats for refill is LineSize/RefillWidth
|
||||
int unsigned RefillWidth;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface simple_cache_types_if #(
|
||||
parameter sc::cfg_t cfg = 0
|
||||
)();
|
||||
|
||||
localparam int SC_NUM_LINES = cfg.Capacity / cfg.LineSize;
|
||||
localparam int SC_LINES_PER_WAY = SC_NUM_LINES / cfg.Associativity;
|
||||
localparam int SC_BLOCK_BITS = $clog2(cfg.LineSize);
|
||||
localparam int SC_ROW_BITS = $clog2(SC_LINES_PER_WAY);
|
||||
localparam int SC_TAG_BITS = cfg.AddrBits - SC_ROW_BITS - SC_BLOCK_BITS;
|
||||
localparam int SC_DROWS_PER_LINE = cfg.LineSize / cfg.FgWidth;
|
||||
localparam int SC_NUM_DROWS = SC_NUM_LINES * SC_DROWS_PER_LINE;
|
||||
|
||||
endinterface
|
||||
|
||||
interface simple_cache_if #(
|
||||
parameter sc::cfg_t cfg = 0
|
||||
)();
|
||||
simple_cache_types_if #(cfg) types();
|
||||
|
||||
endinterface
|
||||
|
||||
module simple_cache #(parameter sc::cfg_t cfg=0) (
|
||||
simple_cache_if io
|
||||
);
|
||||
|
||||
localparam num_rld_beats = cfg.LineSize / cfg.RefillWidth;
|
||||
localparam num_arrays = cfg.FgWidth / cfg.RefillWidth;
|
||||
localparam dat_array_width = cfg.RefillWidth*8;
|
||||
localparam int SC_DROWS_PER_LINE = io.types.SC_DROWS_PER_LINE;
|
||||
localparam int SC_NUM_LINES = io.types.SC_NUM_LINES;
|
||||
localparam int SC_LINES_PER_WAY = io.types.SC_LINES_PER_WAY;
|
||||
localparam int SC_NUM_DROWS = io.types.SC_NUM_DROWS;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd(SC_DROWS_PER_LINE, 4);
|
||||
`checkd(SC_NUM_LINES, 16);
|
||||
`checkd(SC_LINES_PER_WAY, 8);
|
||||
`checkd(SC_NUM_DROWS, 64);
|
||||
`checkd(num_rld_beats, 8);
|
||||
`checkd(num_arrays, 2);
|
||||
`checkd(dat_array_width, 64);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
module t();
|
||||
|
||||
localparam sc::cfg_t sc_cfg = '{
|
||||
CmdTagBits : $clog2(6),
|
||||
Associativity : 2,
|
||||
Capacity : 1024,
|
||||
LineSize : 64,
|
||||
StateBits : 2,
|
||||
AddrBits : 64,
|
||||
MissQSize : 2,
|
||||
|
||||
FgWidth : 16,
|
||||
RefillWidth : 8
|
||||
};
|
||||
|
||||
simple_cache_if #(sc_cfg) sc_io ();
|
||||
|
||||
simple_cache #(sc_cfg) simple_cache(
|
||||
.io(sc_io)
|
||||
);
|
||||
|
||||
localparam int SC_DROWS_PER_LINE = sc_io.types.SC_DROWS_PER_LINE;
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
`checkd(SC_DROWS_PER_LINE, 4);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// DESCRIPTION:
|
||||
// Combined regression model mixing PIN-assigned type param
|
||||
// (t_interface_derived_type) and nested captured typedef flows
|
||||
// (t_lparam_dep_iface*). Keeps both types in a single file.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} scp_cfg_t;
|
||||
|
||||
interface a_if #(parameter a_p = 0)();
|
||||
localparam int LP0 = a_p * 2;
|
||||
typedef logic [LP0-1:0] a_t;
|
||||
endinterface
|
||||
|
||||
interface sct_if #(parameter scp_cfg_t cfg = 0)();
|
||||
localparam int LP0 = cfg.ABits * cfg.BBits;
|
||||
a_if #(LP0) a_if0();
|
||||
typedef a_if0.a_t a_t; // Captured typedef from nested interface
|
||||
endinterface
|
||||
|
||||
interface intf #(
|
||||
parameter type data_t = bit,
|
||||
parameter int arr[2][4]
|
||||
) ();
|
||||
data_t data;
|
||||
logic [$bits(data)-1:0] other_data;
|
||||
endinterface
|
||||
|
||||
module sub #(
|
||||
parameter int width,
|
||||
parameter int arr[2][4]
|
||||
) ();
|
||||
typedef struct packed {
|
||||
logic [3:3] [0:0] [width-1:0] field;
|
||||
} user_type_t;
|
||||
|
||||
// This has a PIN that assigns data_t
|
||||
intf #(
|
||||
.data_t(user_type_t),
|
||||
.arr(arr)
|
||||
) the_intf ();
|
||||
|
||||
logic [width-1:0] signal;
|
||||
|
||||
always_comb begin
|
||||
the_intf.data.field = signal;
|
||||
the_intf.other_data = signal;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t ();
|
||||
localparam scp_cfg_t sc_cfg = '{ABits: 2, BBits: 3};
|
||||
|
||||
sct_if #(sc_cfg) types ();
|
||||
|
||||
sub #(.width(8), .arr('{'{8, 2, 3, 4}, '{1, 2, 3, 4}})) sub8 ();
|
||||
sub #(.width(16), .arr('{'{16, 2, 3, 4}, '{1, 2, 3, 4}})) sub16 ();
|
||||
|
||||
typedef types.a_if0.a_t a_t;
|
||||
typedef types.a_t a2_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd(12, $bits(a_t));
|
||||
`checkd(12, $bits(a2_t));
|
||||
`checkd(8, $bits(sub8.the_intf.data));
|
||||
`checkd(16, $bits(sub16.the_intf.data));
|
||||
|
||||
#1;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// Combined test mixing PIN-assigned type param interface
|
||||
// (t_interface_derived_type) with nested captured typedef/localparam
|
||||
// (t_lparam_dep_iface6).
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} scp_cfg_t;
|
||||
|
||||
interface a_if #(parameter a_p = 0)();
|
||||
localparam int LP0 = a_p;
|
||||
typedef logic [LP0-1:0] a_t;
|
||||
endinterface
|
||||
|
||||
interface sct_if #(parameter scp_cfg_t cfg = 0)();
|
||||
localparam int LP0 = cfg.ABits * cfg.BBits;
|
||||
a_if #(LP0) a_if0();
|
||||
typedef a_if0.a_t a_t;
|
||||
endinterface
|
||||
|
||||
interface sc_if #(parameter scp_cfg_t cfg = 0)();
|
||||
sct_if #(cfg) types();
|
||||
typedef types.a_t a_t;
|
||||
endinterface
|
||||
|
||||
interface intf #(
|
||||
parameter type data_t = bit,
|
||||
parameter int arr[2][4]
|
||||
) ();
|
||||
data_t data;
|
||||
logic [$bits(data)-1:0] other_data;
|
||||
endinterface
|
||||
|
||||
module sub #(
|
||||
parameter int width,
|
||||
parameter int arr[2][4]
|
||||
) ();
|
||||
typedef struct packed {
|
||||
logic [3:3] [0:0] [width-1:0] field;
|
||||
} user_type_t;
|
||||
|
||||
intf #(
|
||||
.data_t(user_type_t),
|
||||
.arr(arr)
|
||||
) the_intf ();
|
||||
|
||||
logic [width-1:0] signal;
|
||||
|
||||
always_comb begin
|
||||
the_intf.data.field = signal;
|
||||
the_intf.other_data = signal;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module sc #(parameter scp_cfg_t cfg=0) (
|
||||
sc_if io
|
||||
);
|
||||
typedef io.a_t a_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd($bits(a_t), 6);
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t ();
|
||||
localparam scp_cfg_t sc_cfg = '{ABits: 2, BBits: 3};
|
||||
|
||||
sc_if #(sc_cfg) sc_io ();
|
||||
sc #(sc_cfg) sc_inst (.io(sc_io));
|
||||
|
||||
sub #(.width(8), .arr('{'{8, 2, 3, 4}, '{1, 2, 3, 4}})) sub8 ();
|
||||
sub #(.width(16), .arr('{'{16, 2, 3, 4}, '{1, 2, 3, 4}})) sub16 ();
|
||||
|
||||
typedef sc_io.types.a_if0.a_t inner_t;
|
||||
typedef sc_io.types.a_t mid_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd($bits(inner_t), 6);
|
||||
`checkd($bits(mid_t), 6);
|
||||
`checkd($bits(sub8.the_intf.data), 8);
|
||||
`checkd($bits(sub16.the_intf.data), 16);
|
||||
|
||||
#1;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// Combined test mixing PIN-assigned type param interface (t_interface_derived_type)
|
||||
// with nested captured typedef/localparam (t_lparam_dep_iface6).
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} scp_cfg_t;
|
||||
|
||||
interface a_if #(parameter a_p = 0)();
|
||||
localparam int LP0 = a_p;
|
||||
typedef logic [LP0-1:0] a_t;
|
||||
endinterface
|
||||
|
||||
interface sct_if #(parameter scp_cfg_t cfg = 0)();
|
||||
localparam int LP0 = cfg.ABits * cfg.BBits;
|
||||
a_if #(LP0) a_if0();
|
||||
typedef a_if0.a_t a_t;
|
||||
endinterface
|
||||
|
||||
interface sc_if #(parameter scp_cfg_t cfg = 0)();
|
||||
sct_if #(cfg) types();
|
||||
typedef types.a_t a_t;
|
||||
endinterface
|
||||
|
||||
interface intf #(
|
||||
parameter type data_t = bit,
|
||||
parameter int arr[2][4]
|
||||
) ();
|
||||
data_t data;
|
||||
logic [$bits(data)-1:0] other_data;
|
||||
endinterface
|
||||
|
||||
module sub #(
|
||||
parameter int width,
|
||||
parameter int arr[2][4]
|
||||
) ();
|
||||
typedef struct packed {
|
||||
logic [3:3] [0:0] [width-1:0] field;
|
||||
} user_type_t;
|
||||
|
||||
intf #(
|
||||
.data_t(user_type_t),
|
||||
.arr(arr)
|
||||
) the_intf ();
|
||||
|
||||
logic [width-1:0] signal;
|
||||
|
||||
always_comb begin
|
||||
the_intf.data.field = signal;
|
||||
the_intf.other_data = signal;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module sc #(parameter scp_cfg_t cfg=0) (
|
||||
sc_if io
|
||||
);
|
||||
typedef io.a_t a_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd($bits(a_t), 6);
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t (input clk);
|
||||
localparam scp_cfg_t sc_cfg = '{ABits: 2, BBits: 3};
|
||||
|
||||
sc_if #(sc_cfg) sc_io ();
|
||||
sc #(sc_cfg) sc_inst (.io(sc_io));
|
||||
|
||||
sub #(.width(8), .arr('{'{8, 2, 3, 4}, '{1, 2, 3, 4}})) sub8 ();
|
||||
|
||||
typedef sc_io.types.a_if0.a_t inner_t;
|
||||
typedef sc_io.types.a_t mid_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd($bits(inner_t), 6);
|
||||
`checkd($bits(mid_t), 6);
|
||||
`checkd($bits(sub8.the_intf.data), 8);
|
||||
|
||||
#1;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
// DESCRIPTION: Verilator: Get agregate type parameter from array of interfaces
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package p1;
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
package p2;
|
||||
typedef struct packed {
|
||||
int unsigned CBits;
|
||||
int unsigned DBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface types_if #(parameter p1::cfg_t cfg=0)();
|
||||
localparam int ABits = cfg.ABits;
|
||||
localparam int BBits = cfg.BBits;
|
||||
|
||||
typedef struct packed {
|
||||
logic [cfg.ABits-1:0] a;
|
||||
logic [cfg.BBits-1:0] b;
|
||||
} a_t;
|
||||
endinterface
|
||||
|
||||
interface io_if #(parameter p1::cfg_t cfg=0)();
|
||||
|
||||
localparam int ABits = cfg.ABits;
|
||||
localparam int BBits = cfg.BBits;
|
||||
|
||||
types_if #(cfg) types ();
|
||||
typedef types.a_t a_t;
|
||||
endinterface
|
||||
|
||||
module modA(
|
||||
io_if io
|
||||
);
|
||||
|
||||
localparam int ABits = io.types.ABits;
|
||||
localparam int BBits = io.types.BBits;
|
||||
|
||||
typedef io.types.a_t a_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd(ABits, 8);
|
||||
`checkd(BBits, 24);
|
||||
`checkd($bits(a_t), 32);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module t ();
|
||||
localparam p2::cfg_t mcfg = '{
|
||||
CBits : 8,
|
||||
DBits : 16
|
||||
};
|
||||
|
||||
localparam p1::cfg_t cfg = '{
|
||||
ABits : mcfg.CBits,
|
||||
BBits : mcfg.CBits + mcfg.DBits
|
||||
};
|
||||
|
||||
io_if #(cfg) modA_io ();
|
||||
|
||||
typedef modA_io.types.a_t a_t;
|
||||
|
||||
modA modA_inst (
|
||||
.io(modA_io)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
`checkd($bits(a_t), 32);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
// DESCRIPTION: Verilator: Get agregate type parameter from array of interfaces
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package sc;
|
||||
typedef struct packed {
|
||||
int unsigned CmdTagBits;
|
||||
int unsigned Associativity;
|
||||
int unsigned Capacity;
|
||||
int unsigned LineSize;
|
||||
int unsigned StateBits;
|
||||
int unsigned AddrBits;
|
||||
int unsigned MissQSize;
|
||||
|
||||
// fetch (hit) width. this must be >= to refill width. FgWidth / RefillWidth is the number of array slices for data.
|
||||
int unsigned FgWidth;
|
||||
// number of expected beats for refill is LineSize/RefillWidth
|
||||
int unsigned RefillWidth;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface simple_cache_types_if #(
|
||||
parameter sc::cfg_t cfg = 0
|
||||
)();
|
||||
|
||||
localparam int SC_NUM_LINES = cfg.Capacity / cfg.LineSize;
|
||||
localparam int SC_LINES_PER_WAY = SC_NUM_LINES / cfg.Associativity;
|
||||
localparam int SC_BLOCK_BITS = $clog2(cfg.LineSize);
|
||||
localparam int SC_ROW_BITS = $clog2(SC_LINES_PER_WAY);
|
||||
localparam int SC_TAG_BITS = cfg.AddrBits - SC_ROW_BITS - SC_BLOCK_BITS;
|
||||
localparam int SC_DROWS_PER_LINE = cfg.LineSize / cfg.FgWidth;
|
||||
localparam int SC_NUM_DROWS = SC_NUM_LINES * SC_DROWS_PER_LINE;
|
||||
|
||||
typedef logic [cfg.AddrBits-1:0] addr_t;
|
||||
typedef logic [cfg.Associativity-1:0] assoc_oh_t;
|
||||
typedef logic [cfg.Associativity-2:0] plru_t;
|
||||
typedef logic [cfg.StateBits-1:0] state_t;
|
||||
typedef logic [cfg.CmdTagBits-1:0] cmd_tag_t;
|
||||
typedef logic [$clog2(cfg.MissQSize)-1:0] missq_tag_t;
|
||||
|
||||
typedef logic [SC_TAG_BITS-1:0] tag_t;
|
||||
typedef logic [SC_ROW_BITS-1:0] row_t;
|
||||
typedef logic [SC_BLOCK_BITS-1:0] block_t;
|
||||
typedef logic [$clog2(SC_NUM_DROWS)-1:0] drow_addr_t;
|
||||
|
||||
typedef struct packed {
|
||||
tag_t tag;
|
||||
row_t row;
|
||||
block_t block;
|
||||
} sc_tag_addr_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic vld;
|
||||
tag_t tag;
|
||||
state_t state;
|
||||
} sc_tag_t;
|
||||
|
||||
typedef struct packed {
|
||||
state_t [cfg.Associativity-1:0] state_v;
|
||||
assoc_oh_t hit_v;
|
||||
assoc_oh_t vld_v;
|
||||
plru_t plru;
|
||||
} sc_tag_status_t;
|
||||
endinterface
|
||||
|
||||
interface simple_cache_if #(
|
||||
parameter sc::cfg_t cfg = 0
|
||||
)();
|
||||
simple_cache_types_if #(cfg) types();
|
||||
|
||||
typedef types.cmd_tag_t cmd_tag_t;
|
||||
typedef types.addr_t addr_t;
|
||||
typedef types.missq_tag_t missq_tag_t;
|
||||
|
||||
endinterface
|
||||
|
||||
module simple_cache #(parameter sc::cfg_t cfg=0) (
|
||||
simple_cache_if io
|
||||
);
|
||||
|
||||
typedef io.types.addr_t addr_t;
|
||||
typedef io.types.cmd_tag_t cmd_tag_t;
|
||||
typedef io.types.drow_addr_t drow_addr_t;
|
||||
typedef io.types.plru_t plru_t;
|
||||
typedef io.types.row_t row_t;
|
||||
typedef io.types.state_t state_t;
|
||||
typedef io.types.sc_tag_addr_t sc_tag_addr_t;
|
||||
typedef io.types.sc_tag_t sc_tag_t;
|
||||
typedef io.types.sc_tag_status_t sc_tag_status_t;
|
||||
|
||||
localparam num_rld_beats = cfg.LineSize / cfg.RefillWidth;
|
||||
localparam num_arrays = cfg.FgWidth / cfg.RefillWidth;
|
||||
localparam dat_array_width = cfg.RefillWidth*8;
|
||||
localparam int SC_DROWS_PER_LINE = io.types.SC_DROWS_PER_LINE;
|
||||
localparam int SC_NUM_LINES = io.types.SC_NUM_LINES;
|
||||
localparam int SC_LINES_PER_WAY = io.types.SC_LINES_PER_WAY;
|
||||
localparam int SC_NUM_DROWS = io.types.SC_NUM_DROWS;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd(SC_DROWS_PER_LINE, 4);
|
||||
`checkd(SC_NUM_LINES, 16);
|
||||
`checkd(SC_LINES_PER_WAY, 8);
|
||||
`checkd(SC_NUM_DROWS, 64);
|
||||
`checkd(num_rld_beats, 8);
|
||||
`checkd(num_arrays, 2);
|
||||
`checkd(dat_array_width, 64);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
module t();
|
||||
|
||||
localparam sc::cfg_t sc_cfg = '{
|
||||
CmdTagBits : $clog2(6),
|
||||
Associativity : 2,
|
||||
Capacity : 1024,
|
||||
LineSize : 64,
|
||||
StateBits : 2,
|
||||
AddrBits : 64,
|
||||
MissQSize : 2,
|
||||
|
||||
FgWidth : 16,
|
||||
RefillWidth : 8
|
||||
};
|
||||
|
||||
simple_cache_if #(sc_cfg) sc_io ();
|
||||
|
||||
simple_cache #(sc_cfg) simple_cache(
|
||||
.io(sc_io)
|
||||
);
|
||||
|
||||
//localparam int SC_DROWS_PER_LINE = sc_io.types.SC_DROWS_PER_LINE;
|
||||
//localparam int SC_NUM_LINES = sc_io.types.SC_NUM_LINES;
|
||||
//localparam int SC_LINES_PER_WAY = sc_io.types.SC_LINES_PER_WAY;
|
||||
//localparam int SC_NUM_DROWS = sc_io.types.SC_NUM_DROWS;
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
//`checkd(SC_DROWS_PER_LINE, 4);
|
||||
//`checkd(SC_NUM_LINES, 16);
|
||||
//`checkd(SC_LINES_PER_WAY, 8);
|
||||
//`checkd(SC_NUM_DROWS, 64);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// DESCRIPTION: Verilator: Get agregate type parameter from array of interfaces
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package scp;
|
||||
typedef struct packed {
|
||||
int unsigned Capacity;
|
||||
int unsigned LineSize;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface sct_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
|
||||
localparam int SC_NUM_LINES = cfg.Capacity / cfg.LineSize;
|
||||
|
||||
typedef logic [(cfg.Capacity / cfg.LineSize)-1:0] sc_num_lines_t;
|
||||
|
||||
typedef logic [SC_NUM_LINES-1:0] sc_num_lines_2_t;
|
||||
|
||||
endinterface
|
||||
|
||||
interface sc_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
sct_if #(cfg) types();
|
||||
|
||||
endinterface
|
||||
|
||||
module sc #(parameter scp::cfg_t cfg=0) (
|
||||
sc_if io
|
||||
);
|
||||
|
||||
localparam int SC_NUM_LINES = io.types.SC_NUM_LINES;
|
||||
|
||||
typedef io.types.sc_num_lines_t sc_num_lines_t;
|
||||
typedef io.types.sc_num_lines_2_t sc_num_lines_2_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
$display("SC_NUM_LINES = %d", SC_NUM_LINES);
|
||||
$display("bits SC_NUM_LINES = %d", $bits(sc_num_lines_t));
|
||||
$display("bits SC_NUM_LINES_2 = %d", $bits(sc_num_lines_2_t));
|
||||
`checkd(SC_NUM_LINES, 16);
|
||||
`checkd($bits(sc_num_lines_t), 16);
|
||||
`checkd($bits(sc_num_lines_2_t), 16);
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
|
||||
localparam scp::cfg_t sc_cfg = '{
|
||||
Capacity : 1024,
|
||||
LineSize : 64
|
||||
};
|
||||
|
||||
sc_if #(sc_cfg) sc_io ();
|
||||
|
||||
sc #(sc_cfg) simple_cache(
|
||||
.io(sc_io)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// DESCRIPTION: Verilator: Get agregate type parameter from array of interfaces
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package scp;
|
||||
typedef struct packed {
|
||||
int unsigned Associativity;
|
||||
int unsigned Capacity;
|
||||
int unsigned LineSize;
|
||||
int unsigned AddrBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface sct_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
|
||||
// this is intentional as I want all the dependencies to be resolved
|
||||
localparam int SC_NUM_LINES = cfg.Capacity / cfg.LineSize;
|
||||
localparam int SC_LINES_PER_WAY = SC_NUM_LINES / cfg.Associativity;
|
||||
localparam int SC_BLOCK_BITS = $clog2(cfg.LineSize);
|
||||
localparam int SC_ROW_BITS = $clog2(SC_LINES_PER_WAY);
|
||||
localparam int SC_TAG_BITS = cfg.AddrBits - SC_ROW_BITS - SC_BLOCK_BITS;
|
||||
|
||||
typedef logic [SC_TAG_BITS-1:0] tag_t;
|
||||
endinterface
|
||||
|
||||
interface sc_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
sct_if #(cfg) types();
|
||||
endinterface
|
||||
|
||||
module sc #(parameter scp::cfg_t cfg=0) (
|
||||
sc_if io
|
||||
);
|
||||
sct_if #(cfg) types();
|
||||
|
||||
typedef io.types.tag_t tag_t;
|
||||
typedef types.tag_t tag2_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd(55, $bits(tag_t));
|
||||
`checkd(55, $bits(tag2_t));
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
localparam scp::cfg_t sc_cfg = '{
|
||||
Associativity : 2,
|
||||
Capacity : 1024,
|
||||
LineSize : 64,
|
||||
AddrBits : 64
|
||||
};
|
||||
|
||||
sc_if #(sc_cfg) sc_io ();
|
||||
|
||||
typedef sc_io.types.tag_t tag_t;
|
||||
|
||||
sc #(sc_cfg) sc(
|
||||
.io(sc_io)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
`checkd(55, $bits(tag_t));
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// DESCRIPTION: Verilator: Get agregate type parameter from array of interfaces
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package scp;
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface a_if #(
|
||||
parameter a_p = 0
|
||||
)();
|
||||
localparam int LP0 = a_p;
|
||||
typedef logic [LP0-1:0] a_t;
|
||||
endinterface
|
||||
|
||||
interface sct_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
// this is intentional as I want all the dependencies to be resolved
|
||||
localparam int LP0 = cfg.ABits * cfg.BBits;
|
||||
|
||||
a_if #(LP0) a_if0();
|
||||
typedef a_if0.a_t a_t;
|
||||
endinterface
|
||||
|
||||
interface sc_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
sct_if #(cfg) types();
|
||||
|
||||
typedef types.a_t a_t;
|
||||
endinterface
|
||||
|
||||
module sc #(parameter scp::cfg_t cfg=0) (
|
||||
sc_if io
|
||||
);
|
||||
|
||||
typedef io.a_t a_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd(6, $bits(a_t));
|
||||
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
localparam scp::cfg_t sc_cfg = '{
|
||||
ABits : 2,
|
||||
BBits : 3
|
||||
};
|
||||
|
||||
sc_if #(sc_cfg) sc_io ();
|
||||
|
||||
sc #(sc_cfg) sc(
|
||||
.io(sc_io)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// DESCRIPTION: Verilator: Get agregate type parameter from array of interfaces
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package scp;
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface a_if #(
|
||||
parameter a_p = 0
|
||||
)();
|
||||
localparam int LP0 = a_p * 2;
|
||||
typedef logic [LP0-1:0] a_t;
|
||||
endinterface
|
||||
|
||||
interface sc_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
|
||||
localparam int LP0 = cfg.ABits * cfg.BBits;
|
||||
a_if #(LP0) types();
|
||||
endinterface
|
||||
|
||||
module sc #(parameter scp::cfg_t cfg=0) (
|
||||
sc_if io
|
||||
);
|
||||
|
||||
typedef io.types.a_t a_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd(12, $bits(a_t));
|
||||
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
localparam scp::cfg_t sc_cfg = '{
|
||||
ABits : 2,
|
||||
BBits : 3
|
||||
};
|
||||
|
||||
sc_if #(sc_cfg) sc_io ();
|
||||
|
||||
sc #(sc_cfg) sc(
|
||||
.io(sc_io)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// DESCRIPTION: Verilator: 3-level nested interface typedef with dependent localparams
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package scp;
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
// Level 3: innermost interface
|
||||
interface a_if #(
|
||||
parameter a_p = 0
|
||||
)();
|
||||
localparam int LP0 = a_p * 2;
|
||||
typedef logic [LP0-1:0] a_t;
|
||||
endinterface
|
||||
|
||||
// Level 2: middle interface
|
||||
interface sct_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
localparam int LP0 = cfg.ABits * cfg.BBits;
|
||||
a_if #(LP0) a_if0();
|
||||
typedef a_if0.a_t a_t;
|
||||
endinterface
|
||||
|
||||
// Level 1: outermost interface
|
||||
interface sc_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
sct_if #(cfg) types();
|
||||
typedef types.a_t a_t;
|
||||
endinterface
|
||||
|
||||
module sc #(parameter scp::cfg_t cfg=0) (
|
||||
sc_if io
|
||||
);
|
||||
|
||||
typedef io.a_t a_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
// cfg.ABits=2, cfg.BBits=3 -> LP0=6 -> a_p=6 -> LP0=12 -> a_t is 12 bits
|
||||
`checkd(12, $bits(a_t));
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
localparam scp::cfg_t sc_cfg = '{
|
||||
ABits : 2,
|
||||
BBits : 3
|
||||
};
|
||||
|
||||
sc_if #(sc_cfg) sc_io ();
|
||||
|
||||
sc #(sc_cfg) sc(
|
||||
.io(sc_io)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// DESCRIPTION: Verilator: Multiple dependent localparams in chain
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package scp;
|
||||
typedef struct packed {
|
||||
int unsigned ABits;
|
||||
int unsigned BBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
// Interface with chained localparam dependencies
|
||||
interface a_if #(
|
||||
parameter a_p = 0
|
||||
)();
|
||||
localparam int LP0 = a_p * 2; // LP0 = a_p * 2
|
||||
localparam int LP1 = LP0 + 1; // LP1 = LP0 + 1
|
||||
localparam int LP2 = LP1 * LP0; // LP2 = LP1 * LP0
|
||||
typedef logic [LP2-1:0] a_t;
|
||||
endinterface
|
||||
|
||||
interface sc_if #(
|
||||
parameter scp::cfg_t cfg = 0
|
||||
)();
|
||||
localparam int LP0 = cfg.ABits * cfg.BBits;
|
||||
a_if #(LP0) types();
|
||||
endinterface
|
||||
|
||||
module sc #(parameter scp::cfg_t cfg=0) (
|
||||
sc_if io
|
||||
);
|
||||
|
||||
typedef io.types.a_t a_t;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
// cfg.ABits=2, cfg.BBits=3 -> LP0=6
|
||||
// a_if: a_p=6 -> LP0=12, LP1=13, LP2=156 -> a_t is 156 bits
|
||||
`checkd(156, $bits(a_t));
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
localparam scp::cfg_t sc_cfg = '{
|
||||
ABits : 2,
|
||||
BBits : 3
|
||||
};
|
||||
|
||||
sc_if #(sc_cfg) sc_io ();
|
||||
|
||||
sc #(sc_cfg) sc(
|
||||
.io(sc_io)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# Verifies that skipWidthForTemplateStruct fires in V3Param::cellPinCleanup
|
||||
# when struct typedefs from a nested parameterized interface are passed as
|
||||
# type parameters through two levels of interface nesting.
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile(v_flags2=["--binary --stats"])
|
||||
|
||||
test.file_grep(test.stats, r'Param, Template struct width skips\s+(\d+)', 2)
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// DESCRIPTION: Verilator: Test type parameter from interface struct
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// Exercises skipWidthForTemplateStruct in V3Param::cellPinCleanup.
|
||||
//
|
||||
// Pattern: nested parameterized interfaces with struct type parameters:
|
||||
// 1. Parameterized inner interface (inner_if) defines struct typedefs
|
||||
// 2. Outer interface (outer_if) contains a nested inner_if instance
|
||||
// 3. A module takes the outer interface as a port and creates typedefs
|
||||
// through two levels of nesting: port.inner.req_t
|
||||
// 4. Those struct typedefs are passed as type parameters to an inner module
|
||||
// 5. A separate module creates inner_if clones with different configs,
|
||||
// ensuring inner_if gets parameterizedTemplate()=true before the
|
||||
// type parameter pins are processed
|
||||
|
||||
package cfg_pkg;
|
||||
typedef struct packed {
|
||||
int unsigned IdBits;
|
||||
int unsigned DataBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
// Parameterized inner interface with struct typedefs
|
||||
interface inner_if #(parameter cfg_pkg::cfg_t cfg = '0);
|
||||
typedef struct packed {
|
||||
logic [cfg.IdBits-1:0] id;
|
||||
logic [cfg.DataBits-1:0] data;
|
||||
} req_t;
|
||||
typedef struct packed {
|
||||
logic [cfg.IdBits-1:0] id;
|
||||
logic [1:0] resp;
|
||||
} resp_t;
|
||||
req_t req;
|
||||
resp_t resp;
|
||||
endinterface
|
||||
|
||||
// Outer interface containing a nested inner_if
|
||||
interface outer_if #(parameter cfg_pkg::cfg_t cfg = '0);
|
||||
inner_if #(cfg) inner();
|
||||
endinterface
|
||||
|
||||
// Module with type parameters (consumer of struct typedefs)
|
||||
module typed_mod #(
|
||||
parameter type req_t = logic,
|
||||
parameter type resp_t = logic
|
||||
)(
|
||||
input logic clk
|
||||
);
|
||||
req_t r;
|
||||
resp_t s;
|
||||
assign r = '0;
|
||||
assign s = '0;
|
||||
endmodule
|
||||
|
||||
// Wrapper: takes outer_if ports, typedefs through two-level nesting,
|
||||
// passes as type parameters to typed_mod
|
||||
module wrap_mod #(parameter int NUM = 1)(
|
||||
input logic clk,
|
||||
outer_if ports [NUM]
|
||||
);
|
||||
typedef ports[0].inner.req_t local_req_t;
|
||||
typedef ports[0].inner.resp_t local_resp_t;
|
||||
typed_mod #(.req_t(local_req_t), .resp_t(local_resp_t)) u_sub(.clk(clk));
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
logic clk = 0;
|
||||
localparam cfg_pkg::cfg_t CFG_A = '{IdBits: 4, DataBits: 32};
|
||||
localparam cfg_pkg::cfg_t CFG_B = '{IdBits: 8, DataBits: 64};
|
||||
|
||||
// Force inner_if to be cloned with different configs first
|
||||
inner_if #(CFG_A) early_a();
|
||||
inner_if #(CFG_B) early_b();
|
||||
assign early_a.req = '0;
|
||||
assign early_a.resp = '0;
|
||||
assign early_b.req = '0;
|
||||
assign early_b.resp = '0;
|
||||
|
||||
outer_if #(CFG_A) io [2] ();
|
||||
wrap_mod #(.NUM(2)) u_wrap(.clk(clk), .ports(io));
|
||||
|
||||
initial begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// DESCRIPTION: Verilator: Regression for prelim ASCRANGE on cfg-based interface typedefs
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package axis;
|
||||
typedef struct packed {
|
||||
int unsigned DataWidth;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface axis_if #(parameter axis::cfg_t cfg = '0)();
|
||||
typedef logic [cfg.DataWidth-1:0] tdata_t;
|
||||
endinterface
|
||||
|
||||
module axis_chan #(
|
||||
parameter axis::cfg_t chan_cfg = '0
|
||||
) ();
|
||||
axis_if #(chan_cfg) axis_channel_io();
|
||||
typedef axis_channel_io.tdata_t data_t;
|
||||
localparam int kWidth = $bits(data_t);
|
||||
initial begin
|
||||
#1;
|
||||
`checkd(kWidth,32);
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
localparam axis::cfg_t axis_chan_cfg = '{DataWidth: 32};
|
||||
axis_chan #(.chan_cfg(axis_chan_cfg)) u_chan();
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
// DESCRIPTION: Minimal test for sibling interface typedef resolution
|
||||
// This is the SIMPLEST case that demonstrates the t_lparam_dep_iface10 failure pattern:
|
||||
// - Two sibling cells of the same interface type with DIFFERENT parameters
|
||||
// - A module that accesses typedefs from BOTH siblings
|
||||
//
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package rial;
|
||||
|
||||
// Configuration structure
|
||||
typedef struct packed {
|
||||
// CCA Parameters
|
||||
int unsigned NumDd;
|
||||
// CC Parameters
|
||||
int unsigned DDNumStuff;
|
||||
int unsigned DDNumStuffThreads;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
package cb;
|
||||
typedef struct packed {
|
||||
int unsigned XdatSize; // raw packet data size
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface ccia_types_if #(parameter rial::cfg_t cfg=0)();
|
||||
|
||||
// 'base' types
|
||||
typedef logic [$clog2(cfg.DDNumStuff)-1:0] wave_index_t;
|
||||
|
||||
// types for tb
|
||||
typedef struct packed {
|
||||
logic [3:0] e_cmd;
|
||||
logic en;
|
||||
logic csr;
|
||||
wave_index_t wave_index;
|
||||
logic [11:0] reg_addr;
|
||||
logic [64-(4+1+1+$clog2(cfg.DDNumStuff)+12)-1:0] pad0;
|
||||
} tl_reg_cmd_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic [63:0] raw;
|
||||
} tl_addr_cmd_t;
|
||||
|
||||
typedef union packed {
|
||||
tl_reg_cmd_t rcmd;
|
||||
tl_addr_cmd_t acmd;
|
||||
} tl_data_fld_t;
|
||||
|
||||
typedef union packed {
|
||||
tl_data_fld_t [cfg.DDNumStuffThreads-1:0] d_a;
|
||||
} cmd_data_t;
|
||||
|
||||
typedef struct packed {
|
||||
cmd_data_t d;
|
||||
} cmd_beat_t;
|
||||
|
||||
endinterface
|
||||
|
||||
module rial_top #(
|
||||
parameter rial::cfg_t aer_cfg=0
|
||||
)();
|
||||
|
||||
// for the types
|
||||
ccia_types_if #(aer_cfg) ccia_types();
|
||||
|
||||
// genvars and locally defined types
|
||||
typedef ccia_types.cmd_beat_t cmd_beat_t;
|
||||
|
||||
// CB and RBUS
|
||||
localparam cb::cfg_t cb_cfg = '{
|
||||
XdatSize:$bits(cmd_beat_t)
|
||||
};
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd($bits(ccia_types.tl_data_fld_t), 64);
|
||||
`checkd($bits(ccia_types.cmd_data_t), 512);
|
||||
`checkd($bits(cmd_beat_t), 512);
|
||||
`checkd(cb_cfg.XdatSize, 512);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
// SOC Top w/IO and SOC configuration
|
||||
module rial_wrap();
|
||||
|
||||
parameter rial::cfg_t aer_cfg = '{
|
||||
NumDd : 3,
|
||||
// CC Parameters
|
||||
DDNumStuff : 4,
|
||||
DDNumStuffThreads : 8
|
||||
};
|
||||
|
||||
// DUT
|
||||
rial_top #(
|
||||
.aer_cfg(aer_cfg)
|
||||
) rial_top();
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
|
||||
// DESCRIPTION: Minimal test for sibling interface typedef resolution
|
||||
// This is the SIMPLEST case that demonstrates the t_lparam_dep_iface10 failure pattern:
|
||||
// - Two sibling cells of the same interface type with DIFFERENT parameters
|
||||
// - A module that accesses typedefs from BOTH siblings
|
||||
//
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package TestPkg;
|
||||
// Create a struct that results in 525 bits like in aerial_wrap
|
||||
typedef struct packed {
|
||||
logic [31:0] field1;
|
||||
logic [31:0] field2;
|
||||
logic [31:0] field3;
|
||||
logic [31:0] field4;
|
||||
logic [31:0] field5;
|
||||
logic [31:0] field6;
|
||||
logic [31:0] field7;
|
||||
logic [31:0] field8;
|
||||
logic [31:0] field9;
|
||||
logic [31:0] field10;
|
||||
logic [31:0] field11;
|
||||
logic [31:0] field12;
|
||||
logic [31:0] field13;
|
||||
logic [31:0] field14;
|
||||
logic [31:0] field15;
|
||||
logic [31:0] field16;
|
||||
logic [12:0] field17; // 525 bits total (16*32 + 13)
|
||||
} cmd_beat_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic [31:0] Rids;
|
||||
logic [31:0] Pids;
|
||||
logic [31:0] Fnum;
|
||||
logic [31:0] XdatSize; // 32-bit field
|
||||
} cfg_t;
|
||||
|
||||
// This pattern assignment should trigger the error
|
||||
// The issue is that $bits(cmd_beat_t) evaluation during DepGraph causes corruption
|
||||
// where the pattern literal gets a 128-bit constant instead of proper 32-bit assignment
|
||||
// Note: cmd_beat_t is referenced directly, not through a localparam type alias
|
||||
localparam cfg_t cb_cfg = '{
|
||||
Rids : 32'h1,
|
||||
Pids : 32'h2,
|
||||
Fnum : 32'h3,
|
||||
XdatSize : $bits(cmd_beat_t) // Should be 525, but gets corrupted
|
||||
};
|
||||
endpackage
|
||||
|
||||
module TestMod;
|
||||
import TestPkg::*;
|
||||
|
||||
initial begin
|
||||
$display("XdatSize = %d", cb_cfg.XdatSize);
|
||||
`checkd(cb_cfg.XdatSize, 525);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// DESCRIPTION: Test for $bits() of interface typedef used as parameter value
|
||||
// This reproduces the issue from axis_upsizer.sv:186
|
||||
// The issue: $bits(op_pkt_t) where op_pkt_t is a typedef from an interface port
|
||||
// can't be converted to constant because the PARAMTYPEDTYPE's dtype isn't resolved
|
||||
|
||||
// Interface with a packed struct typedef
|
||||
interface axis_if #(
|
||||
parameter int DataWidth = 8
|
||||
);
|
||||
typedef struct packed {
|
||||
logic [DataWidth-1:0] data;
|
||||
logic valid;
|
||||
} pkt_t;
|
||||
|
||||
logic [DataWidth-1:0] tdata;
|
||||
logic tvalid;
|
||||
logic tready;
|
||||
|
||||
modport initiator (output tdata, tvalid, input tready);
|
||||
modport target (input tdata, tvalid, output tready);
|
||||
endinterface
|
||||
|
||||
// Simple buffer module that takes a width parameter
|
||||
module skid_buffer #(
|
||||
parameter int p_width = 8
|
||||
) (
|
||||
input logic clk,
|
||||
input logic [p_width-1:0] data_i,
|
||||
output logic [p_width-1:0] data_o
|
||||
);
|
||||
always_ff @(posedge clk) data_o <= data_i;
|
||||
endmodule
|
||||
|
||||
// Module that uses $bits() of an interface typedef as a parameter
|
||||
module axis_upsizer #(
|
||||
parameter int p_has_skid = 1
|
||||
) (
|
||||
input logic clk,
|
||||
axis_if.initiator op_io
|
||||
);
|
||||
// Typedef from interface port
|
||||
typedef op_io.pkt_t op_pkt_t;
|
||||
|
||||
op_pkt_t op_pkt_int;
|
||||
|
||||
generate
|
||||
if (p_has_skid>0) begin : gen_skid
|
||||
op_pkt_t skid_src_pkt;
|
||||
|
||||
// This is the problematic line - $bits(op_pkt_t) used as parameter
|
||||
// The PARAMTYPEDTYPE for op_pkt_t has REQUIREDTYPE that needs resolution
|
||||
skid_buffer #(.p_width($bits(op_pkt_t))) skid (
|
||||
.clk(clk),
|
||||
.data_i(op_pkt_int),
|
||||
.data_o(skid_src_pkt)
|
||||
);
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
||||
module top;
|
||||
logic clk;
|
||||
|
||||
axis_if #(.DataWidth(32)) op_if();
|
||||
|
||||
axis_upsizer #(.p_has_skid(1)) u_upsizer (
|
||||
.clk(clk),
|
||||
.op_io(op_if.initiator)
|
||||
);
|
||||
|
||||
initial begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// DESCRIPTION: Verilator: Test for cloned RefDType classOrPackagep fix
|
||||
//
|
||||
// This test verifies that when parameterized classes are cloned, the RefDType
|
||||
// nodes that reference typedefs within the class get their classOrPackagep
|
||||
// and typedefp updated to point to the cloned class, not the template class.
|
||||
//
|
||||
// This file is part of the Verilator regression test suite.
|
||||
//
|
||||
|
||||
// A registry class that returns its own type
|
||||
class uvm_object_registry #(type T = int, string Tname = "<unknown>");
|
||||
typedef uvm_object_registry#(T, Tname) this_type;
|
||||
|
||||
static function this_type get();
|
||||
static this_type m_inst;
|
||||
if (m_inst == null) m_inst = new();
|
||||
return m_inst;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// A pool class that has a nested type_id typedef pointing to the registry
|
||||
// The key pattern: type_id is a typedef to uvm_object_registry parameterized with THIS class
|
||||
class uvm_object_string_pool #(type T = int);
|
||||
typedef uvm_object_string_pool#(T) this_type;
|
||||
typedef uvm_object_registry#(uvm_object_string_pool#(T)) type_id;
|
||||
|
||||
// This function's return type references type_id - after cloning,
|
||||
// the RefDType for the return type must point to THIS class's type_id,
|
||||
// not the template class's type_id
|
||||
static function type_id get_type();
|
||||
return type_id::get();
|
||||
endfunction
|
||||
|
||||
static function T get_global();
|
||||
this_type gpool = new();
|
||||
return gpool.get();
|
||||
endfunction
|
||||
|
||||
virtual function T get();
|
||||
T result;
|
||||
return result;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Simple wrapper classes to create different specializations
|
||||
class uvm_queue #(type T = int);
|
||||
endclass
|
||||
|
||||
class uvm_event #(type T = int);
|
||||
endclass
|
||||
|
||||
// Create two different specializations of uvm_object_string_pool
|
||||
// Each should get its own type_id pointing to a different registry specialization
|
||||
typedef uvm_object_string_pool#(uvm_event#(int)) uvm_event_pool;
|
||||
typedef uvm_object_string_pool#(uvm_queue#(string)) uvm_queue_pool;
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
// Get the type_id::get() for both pool types
|
||||
// Before the fix, both would incorrectly return the same registry type
|
||||
// After the fix, each returns its own correctly-specialized registry
|
||||
|
||||
uvm_object_registry#(uvm_object_string_pool#(uvm_event#(int))) event_reg;
|
||||
uvm_object_registry#(uvm_object_string_pool#(uvm_queue#(string))) queue_reg;
|
||||
|
||||
event_reg = uvm_event_pool::get_type();
|
||||
queue_reg = uvm_queue_pool::get_type();
|
||||
|
||||
if (event_reg != null && queue_reg != null) begin
|
||||
$write("*-* All Coverage Coverage *-*\n");
|
||||
end
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
// DESCRIPTION: Verilator: TRULY BLENDED test for interface typedef resolution
|
||||
// This test BLENDS both patterns into a single interacting structure:
|
||||
// - Sibling cells (like t_lparam_dep_iface10)
|
||||
// - Nested interface chains (like aerial_wrap)
|
||||
// - COMBINED: Sibling cells that EACH contain nested interface chains
|
||||
//
|
||||
// The key test: A module accesses typedefs from TWO sibling nested interface
|
||||
// chains, and each must resolve to the correct parameterized type.
|
||||
//
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
typedef struct packed {
|
||||
int unsigned AddrBits;
|
||||
int unsigned DataBits;
|
||||
int unsigned IdBits;
|
||||
} axi_cfg_t;
|
||||
|
||||
// INNERMOST: Parameterized interface with typedefs
|
||||
interface axi4_if #(parameter axi_cfg_t cfg = 0)();
|
||||
localparam int unsigned AddrBits = cfg.AddrBits * 2;
|
||||
localparam int unsigned DataBits = cfg.DataBits * 2;
|
||||
localparam int unsigned IdBits = cfg.IdBits * 2;
|
||||
|
||||
typedef logic [AddrBits-1:0] addr_t;
|
||||
typedef logic [DataBits-1:0] data_t;
|
||||
typedef logic [IdBits-1:0] id_t;
|
||||
|
||||
typedef struct packed {
|
||||
id_t id;
|
||||
addr_t addr;
|
||||
} ar_chan_t;
|
||||
|
||||
typedef struct packed {
|
||||
id_t id;
|
||||
data_t data;
|
||||
} r_chan_t;
|
||||
|
||||
ar_chan_t ar;
|
||||
r_chan_t r;
|
||||
endinterface
|
||||
|
||||
// MIDDLE: Interface that wraps axi4_if and re-exports its typedefs
|
||||
interface tlb_io_if #(parameter axi_cfg_t axi_cfg = 0)();
|
||||
axi4_if #(.cfg(axi_cfg)) axi_tlb_io();
|
||||
|
||||
// Re-export typedefs from nested interface
|
||||
typedef axi_tlb_io.r_chan_t r_chan_t;
|
||||
typedef axi_tlb_io.ar_chan_t ar_chan_t;
|
||||
endinterface
|
||||
|
||||
// OUTER: Interface with TWO SIBLING tlb_io_if instances with DIFFERENT params
|
||||
// This is the BLENDED pattern: sibling cells + nested chains
|
||||
interface cca_io_if #(
|
||||
parameter axi_cfg_t axi_cfg_a = 0,
|
||||
parameter axi_cfg_t axi_cfg_b = 0
|
||||
)();
|
||||
// SIBLING CELLS - same interface type, DIFFERENT params
|
||||
tlb_io_if #(.axi_cfg(axi_cfg_a)) tlb_io_a();
|
||||
tlb_io_if #(.axi_cfg(axi_cfg_b)) tlb_io_b();
|
||||
|
||||
// Re-export from each sibling (these should be DIFFERENT types)
|
||||
typedef tlb_io_a.r_chan_t r_chan_a_t;
|
||||
typedef tlb_io_b.r_chan_t r_chan_b_t;
|
||||
endinterface
|
||||
|
||||
// MODULE: Accesses typedefs from BOTH sibling nested chains via interface port
|
||||
// This is the CRITICAL test - must distinguish between tlb_io_a and tlb_io_b
|
||||
module cca_xbar (
|
||||
cca_io_if cca_io
|
||||
);
|
||||
// Access typedefs through SIBLING nested interface chains
|
||||
// These MUST resolve to DIFFERENT types based on the different params
|
||||
typedef cca_io.tlb_io_a.r_chan_t m_r_chan_a_t; // From axi_cfg_a
|
||||
typedef cca_io.tlb_io_b.r_chan_t m_r_chan_b_t; // From axi_cfg_b
|
||||
typedef cca_io.tlb_io_a.ar_chan_t m_ar_chan_a_t;
|
||||
typedef cca_io.tlb_io_b.ar_chan_t m_ar_chan_b_t;
|
||||
|
||||
m_r_chan_a_t r_data_a;
|
||||
m_r_chan_b_t r_data_b;
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
// axi_cfg_a: AddrBits=32, DataBits=64, IdBits=4
|
||||
// r_chan_t = id(4) + data(64) = 68 bits * 2 = 136 bits
|
||||
// ar_chan_t = id(4) + addr(32) = 36 bits * 2 = 72 bits
|
||||
`checkd($bits(m_r_chan_a_t), 136);
|
||||
`checkd($bits(m_ar_chan_a_t), 72);
|
||||
|
||||
// axi_cfg_b: AddrBits=40, DataBits=128, IdBits=8
|
||||
// r_chan_t = id(8) + data(128) = 136 bits * 2 = 272 bits
|
||||
// ar_chan_t = id(8) + addr(40) = 48 bits * 2 = 96 bits
|
||||
`checkd($bits(m_r_chan_b_t), 272);
|
||||
`checkd($bits(m_ar_chan_b_t), 96);
|
||||
end
|
||||
endmodule
|
||||
|
||||
// TOP MODULE
|
||||
module t();
|
||||
localparam axi_cfg_t cfg_a = '{AddrBits: 32, DataBits: 64, IdBits: 4};
|
||||
localparam axi_cfg_t cfg_b = '{AddrBits: 40, DataBits: 128, IdBits: 8};
|
||||
|
||||
cca_io_if #(.axi_cfg_a(cfg_a), .axi_cfg_b(cfg_b)) cca_io();
|
||||
cca_xbar xbar(.cca_io(cca_io));
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
# Stats-variant of t_paramgraph_comined_iface: verifies IfaceCapture
|
||||
# statistics for combined sibling + nested interface typedef patterns.
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.top_filename = "t/t_paramgraph_comined_iface.v"
|
||||
|
||||
test.compile(v_flags2=["--binary --stats"])
|
||||
|
||||
test.file_grep(test.stats, r'IfaceCapture, Entries total\s+(\d+)', 18)
|
||||
test.file_grep(test.stats, r'IfaceCapture, Entries template\s+(\d+)', 8)
|
||||
test.file_grep(test.stats, r'IfaceCapture, Entries cloned\s+(\d+)', 10)
|
||||
test.file_grep(test.stats, r'IfaceCapture, Ledger fixups in V3Param\s+(\d+)', 10)
|
||||
test.file_grep(test.stats, r'IfaceCapture, Wrong-clone refs fixed\s+(\d+)', 10)
|
||||
test.file_grep(test.stats, r'IfaceCapture, Dead refs fixed in modules\s+(\d+)', 6)
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// Minimal testcase for depgraph resolution with arrayed interface ports
|
||||
// and typedefs pulled from interface instances.
|
||||
//
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package axi_pkg;
|
||||
typedef struct packed {
|
||||
int IdBits;
|
||||
int DataBits;
|
||||
int UserBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface axi4_if #(parameter axi_pkg::cfg_t cfg = '0)();
|
||||
typedef logic [cfg.IdBits-1:0] id_t;
|
||||
typedef logic [cfg.DataBits-1:0] data_t;
|
||||
typedef logic [cfg.UserBits-1:0] user_t;
|
||||
|
||||
typedef struct packed {
|
||||
id_t id;
|
||||
data_t data;
|
||||
user_t user;
|
||||
} req_t;
|
||||
endinterface
|
||||
|
||||
module sink #(parameter int N = 1)(axi4_if tgt_ports [N-1:0]);
|
||||
localparam type req_t = tgt_ports[0].req_t;
|
||||
req_t rq;
|
||||
endmodule
|
||||
|
||||
module top;
|
||||
localparam axi_pkg::cfg_t cfg = '{IdBits:4, DataBits:32, UserBits:2};
|
||||
axi4_if #(.cfg(cfg)) tgt_ports [1:0]();
|
||||
|
||||
sink #(.N(2)) u_sink(.tgt_ports(tgt_ports));
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd($bits(tgt_ports[0].id_t), 4);
|
||||
`checkd($bits(tgt_ports[0].data_t), 32);
|
||||
`checkd($bits(tgt_ports[0].user_t), 2);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// DESCRIPTION:
|
||||
// Minimal testcase for depgraph interface typedef resolution
|
||||
// Derived from aicc_types_if/axis_if ASCRANGE warnings
|
||||
//
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package aerial;
|
||||
typedef struct packed {
|
||||
int NumCc;
|
||||
int CCNumWaves;
|
||||
int CCNumIds;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface aicc_types_if #(parameter aerial::cfg_t cfg = '0)();
|
||||
typedef logic [$clog2(cfg.NumCc)-1:0] cc_index_t;
|
||||
typedef logic [$clog2(cfg.CCNumIds)-1:0] trans_id_t;
|
||||
endinterface
|
||||
|
||||
module child(aicc_types_if types);
|
||||
localparam type cc_index_t = types.cc_index_t;
|
||||
localparam type trans_id_t = types.trans_id_t;
|
||||
cc_index_t cc_idx;
|
||||
trans_id_t tr_id;
|
||||
endmodule
|
||||
|
||||
module top;
|
||||
localparam aerial::cfg_t aer_cfg = '{NumCc:4, CCNumWaves:2, CCNumIds:8};
|
||||
aicc_types_if #(.cfg(aer_cfg)) types();
|
||||
child u_child(.types(types));
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
`checkd($bits(types.cc_index_t), 2);
|
||||
`checkd($bits(types.trans_id_t), 3);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// Minimal testcase to see if unused modules with interface ports
|
||||
// still trigger ASCRANGE when interface params are defaulted.
|
||||
//
|
||||
|
||||
package axi4;
|
||||
typedef struct packed {
|
||||
int IdBits;
|
||||
int AddrBits;
|
||||
int DataBits;
|
||||
int UserBits;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface axi4_if #(parameter axi4::cfg_t cfg = '0)();
|
||||
typedef logic [cfg.AddrBits-1:0] addr_t;
|
||||
typedef logic [cfg.DataBits-1:0] data_t;
|
||||
typedef logic [cfg.DataBits/8-1:0] strb_t;
|
||||
typedef logic [cfg.UserBits-1:0] user_t;
|
||||
typedef logic [cfg.IdBits-1:0] id_t;
|
||||
|
||||
typedef struct packed {
|
||||
id_t id;
|
||||
addr_t addr;
|
||||
user_t user;
|
||||
} aw_chan_t;
|
||||
endinterface
|
||||
|
||||
module dead_mod(
|
||||
axi4_if axi_io
|
||||
);
|
||||
typedef axi_io.addr_t addr_t;
|
||||
typedef axi_io.data_t data_t;
|
||||
typedef axi_io.strb_t strb_t;
|
||||
|
||||
addr_t addr_d;
|
||||
data_t data_d;
|
||||
strb_t strb_d;
|
||||
endmodule
|
||||
|
||||
module dead_top;
|
||||
localparam axi4::cfg_t cfg = '{IdBits:4, AddrBits:32, DataBits:64, UserBits:2};
|
||||
axi4_if #(.cfg(cfg)) axi_io();
|
||||
|
||||
dead_mod u_dead(.axi_io(axi_io));
|
||||
endmodule
|
||||
|
||||
module top;
|
||||
dead_top dead_top();
|
||||
initial begin
|
||||
#1;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package a_pkg;
|
||||
typedef struct packed {
|
||||
int unsigned a;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface depgraph_if #(a_pkg::cfg_t cfg=0)();
|
||||
typedef logic [cfg.a-1:0] byte_t;
|
||||
typedef struct packed {
|
||||
byte_t a;
|
||||
} pair_t;
|
||||
endinterface
|
||||
|
||||
module a_mod(
|
||||
depgraph_if ifc
|
||||
);
|
||||
typedef ifc.pair_t pair_t;
|
||||
|
||||
localparam p_a = $bits(pair_t);
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkd($bits(pair_t),8);
|
||||
`checkd(p_a, 8);
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t();
|
||||
localparam a_pkg::cfg_t cfg = '{
|
||||
a: 8
|
||||
};
|
||||
|
||||
depgraph_if #(cfg) ifc();
|
||||
a_mod #() a_mod_0(
|
||||
.ifc(ifc)
|
||||
);
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
package a_pkg;
|
||||
typedef struct packed {
|
||||
int unsigned a;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
package b_pkg;
|
||||
typedef struct packed {
|
||||
int unsigned a;
|
||||
} cfg_t;
|
||||
endpackage
|
||||
|
||||
interface depgraph_if #(a_pkg::cfg_t cfg=0)();
|
||||
typedef logic [cfg.a-1:0] byte_t;
|
||||
typedef logic [cfg.a*2-1:0] half_t;
|
||||
typedef struct packed {
|
||||
byte_t a;
|
||||
half_t b;
|
||||
} pair_t;
|
||||
typedef union packed {
|
||||
pair_t p;
|
||||
logic [23:0] flat;
|
||||
} pair_u_t;
|
||||
endinterface
|
||||
|
||||
module t();
|
||||
localparam a_pkg::cfg_t cfg = '{
|
||||
a: 8
|
||||
};
|
||||
|
||||
depgraph_if #(cfg) ifc();
|
||||
|
||||
typedef ifc.pair_u_t pair_u_t;
|
||||
|
||||
localparam b_pkg::cfg_t cfg_b = '{
|
||||
a:$bits(pair_u_t)
|
||||
};
|
||||
|
||||
initial begin
|
||||
#2;
|
||||
`checkd(cfg_b.a, 24);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue