mirror of
https://github.com/verilator/verilator.git
synced 2026-09-03 16:28:53 +02:00
This commit is contained in:
+59
-8
@@ -800,7 +800,8 @@ class ParamProcessor final {
|
||||
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
|
||||
// No parent portion to verify - startModp itself must be the expected parent
|
||||
if (lastDot == string::npos) return startModp == expectModp;
|
||||
const string parentPath = cellPath.substr(0, lastDot);
|
||||
const AstNodeModule* const resolvedp
|
||||
= V3LinkDotIfaceCapture::followCellPath(startModp, parentPath);
|
||||
@@ -869,6 +870,7 @@ class ParamProcessor final {
|
||||
<< (correctModp ? correctModp->name() : "<null>")
|
||||
<< endl);
|
||||
if (!correctModp || correctModp->dead()) return;
|
||||
if (correctModp->parameterizedTemplate()) return;
|
||||
|
||||
bool fixed = false;
|
||||
if (refp->typedefp()) {
|
||||
@@ -1292,8 +1294,10 @@ class ParamProcessor final {
|
||||
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()) {
|
||||
// Skip if owned by a parameterized interface (template or not
|
||||
// yet cloned). hasGParam() covers interfaces that haven't
|
||||
// been cloned yet (parameterizedTemplate() not set).
|
||||
if (ownerModp && VN_IS(ownerModp, Iface) && ownerModp->hasGParam()) {
|
||||
skipWidthForTemplateStruct = true;
|
||||
V3Stats::addStatSum("Param, Template struct width skips", 1);
|
||||
UINFO(9, "SKIP-WIDTH-TEMPLATE: struct="
|
||||
@@ -1776,6 +1780,28 @@ class ParamProcessor final {
|
||||
}
|
||||
|
||||
public:
|
||||
// After an interface cell inside parentModp has been deparameterized
|
||||
// (rewired from template to clone), retarget REFDTYPEs that still
|
||||
// reference the old template's types so that $bits(iface_typedef)
|
||||
// evaluates with the clone's widths.
|
||||
void retargetIfaceRefs(AstNodeModule* parentModp, const string& cellName) {
|
||||
AstNodeModule* const correctModp
|
||||
= V3LinkDotIfaceCapture::followCellPath(parentModp, cellName);
|
||||
if (!correctModp || correctModp->dead() || correctModp->parameterizedTemplate()) return;
|
||||
V3LinkDotIfaceCapture::forEach([&](const V3LinkDotIfaceCapture::CapturedEntry& entry) {
|
||||
if (!entry.refp || entry.cloneCellPath.empty()) return;
|
||||
if (entry.cellPath != cellName) return;
|
||||
// Identity check: only retarget REFDTYPEs that actually live
|
||||
// inside parentModp. Multiple clones share origName so name
|
||||
// matching alone would let one clone overwrite another's refs.
|
||||
if (V3LinkDotIfaceCapture::findOwnerModule(entry.refp) != parentModp) return;
|
||||
if (retargetRefToModule(entry, correctModp)) {
|
||||
UINFO(9, "retargetIfaceRefs: " << entry.refp << " -> "
|
||||
<< correctModp->prettyNameQ() << endl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
AstNodeModule* nodeDeparam(AstNode* nodep, AstNodeModule* srcModp, AstNodeModule* modp,
|
||||
const string& someInstanceName) {
|
||||
// Return new or reused de-parameterized module
|
||||
@@ -1895,7 +1921,11 @@ public:
|
||||
// STATE - across all visitors
|
||||
std::vector<AstClass*> m_paramClasses; // Parameterized classes
|
||||
std::vector<AstDot*> m_dots; // Dot references to process
|
||||
std::multimap<int, AstNodeModule*> m_workQueueNext; // Modules left to process
|
||||
// Work queue keyed by (!isIface, level) so interfaces are always processed
|
||||
// before non-interfaces. This ensures interface clones have their types
|
||||
// properly widthed before any module that references those types.
|
||||
using WQKey = std::pair<bool, int>;
|
||||
std::multimap<WQKey, AstNodeModule*> m_workQueueNext; // Modules left to process
|
||||
// Map from AstNodeModule to set of all AstNodeModules that instantiates it.
|
||||
std::unordered_map<AstNodeModule*, std::unordered_set<AstNodeModule*>> m_parentps;
|
||||
};
|
||||
@@ -1930,7 +1960,7 @@ class ParamVisitor final : public VNVisitor {
|
||||
|
||||
void processWorkQ() {
|
||||
UASSERT(!m_iterateModule, "Should not nest");
|
||||
std::multimap<int, AstNodeModule*> workQueue;
|
||||
std::multimap<ParamState::WQKey, AstNodeModule*> workQueue;
|
||||
m_generateHierName = "";
|
||||
m_iterateModule = true;
|
||||
|
||||
@@ -2005,10 +2035,21 @@ class ParamVisitor final : public VNVisitor {
|
||||
|
||||
if (VN_IS(srcModp, Iface)) {
|
||||
logTemplateLeakRefs(modp, srcModp, "after queued nodeDeparam", cellp);
|
||||
// After the interface cell is rewired to its clone,
|
||||
// retarget REFDTYPEs in the parent module that still
|
||||
// reference the template interface's types.
|
||||
if (V3LinkDotIfaceCapture::enabled()) {
|
||||
if (const AstCell* const modCellp = VN_CAST(cellp, Cell)) {
|
||||
if (newModp != srcModp) {
|
||||
m_processor.retargetIfaceRefs(modp, modCellp->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the (now potentially specialized) child module to the work queue
|
||||
workQueue.emplace(newModp->level(), newModp);
|
||||
workQueue.emplace(ParamState::WQKey{!VN_IS(newModp, Iface), newModp->level()},
|
||||
newModp);
|
||||
|
||||
// Add to the hierarchy registry
|
||||
m_state.m_parentps[newModp].insert(modp);
|
||||
@@ -2194,6 +2235,14 @@ class ParamVisitor final : public VNVisitor {
|
||||
// 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());
|
||||
// After the interface cell is rewired to its clone,
|
||||
// retarget REFDTYPEs in the parent module that still
|
||||
// reference the template interface's types. This ensures
|
||||
// $bits(iface_typedef) evaluates correctly when
|
||||
// widthParamsEdit runs on subsequent lparams.
|
||||
if (V3LinkDotIfaceCapture::enabled() && cellp->modp() != srcModp) {
|
||||
m_processor.retargetIfaceRefs(m_modp, cellp->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2225,7 +2274,8 @@ class ParamVisitor final : public VNVisitor {
|
||||
UINFO(4, " MOD-under-MOD. " << nodep);
|
||||
// Delay until current module is done.
|
||||
// processWorkQ() (which we are returning to) will process nodep later
|
||||
m_state.m_workQueueNext.emplace(nodep->level(), nodep);
|
||||
m_state.m_workQueueNext.emplace(
|
||||
ParamState::WQKey{!VN_IS(nodep, Iface), nodep->level()}, nodep);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2233,7 +2283,8 @@ class ParamVisitor final : public VNVisitor {
|
||||
if (nodep->isTop() // Tops
|
||||
|| VN_IS(nodep, Class) // Moved classes
|
||||
|| VN_IS(nodep, Package)) { // Likewise haven't done wrapTopPackages yet
|
||||
m_state.m_workQueueNext.emplace(nodep->level(), nodep);
|
||||
m_state.m_workQueueNext.emplace(
|
||||
ParamState::WQKey{!VN_IS(nodep, Iface), nodep->level()}, nodep);
|
||||
processWorkQ();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user