Use unchanged copy of parameterized class for instantation (#4179)
This commit is contained in:
parent
e095bf1af0
commit
0198a3fc52
|
|
@ -221,7 +221,7 @@ public:
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// Remove parameters from cells and build new modules
|
// Remove parameters from cells and build new modules
|
||||||
|
|
||||||
class ParamProcessor final {
|
class ParamProcessor final : public VNDeleter {
|
||||||
// NODE STATE - Local
|
// NODE STATE - Local
|
||||||
// AstVar::user4() // int Global parameter number (for naming new module)
|
// AstVar::user4() // int Global parameter number (for naming new module)
|
||||||
// // (0=not processed, 1=iterated, but no number,
|
// // (0=not processed, 1=iterated, but no number,
|
||||||
|
|
@ -257,6 +257,9 @@ class ParamProcessor final {
|
||||||
// Generated modules by this visitor is not included
|
// Generated modules by this visitor is not included
|
||||||
V3StringSet m_allModuleNames;
|
V3StringSet m_allModuleNames;
|
||||||
|
|
||||||
|
CloneMap m_originalParams; // Map between parameters of copied parameteized classes and their
|
||||||
|
// original nodes
|
||||||
|
|
||||||
std::map<const V3Hash, int> m_valueMap; // Hash of node hash to param value
|
std::map<const V3Hash, int> m_valueMap; // Hash of node hash to param value
|
||||||
int m_nextValue = 1; // Next value to use in m_valueMap
|
int m_nextValue = 1; // Next value to use in m_valueMap
|
||||||
|
|
||||||
|
|
@ -387,23 +390,20 @@ class ParamProcessor final {
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
void collectPins(CloneMap* clonemapp, AstNodeModule* modp) {
|
void collectPins(CloneMap* clonemapp, AstNodeModule* modp, bool originalIsCopy) {
|
||||||
// Grab all I/O so we can remap our pins later
|
// Grab all I/O so we can remap our pins later
|
||||||
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||||
|
const AstNode* originalParamp = nullptr;
|
||||||
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
|
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
|
||||||
if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) {
|
if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) {
|
||||||
// Cloning saved a pointer to the new node for us, so just follow that link.
|
// Cloning saved a pointer to the new node for us, so just follow that link.
|
||||||
const AstVar* const oldvarp = varp->clonep();
|
originalParamp = varp->clonep();
|
||||||
// UINFO(8,"Clone list 0x"<<hex<<(uint32_t)oldvarp
|
|
||||||
// <<" -> 0x"<<(uint32_t)varp<<endl);
|
|
||||||
clonemapp->emplace(oldvarp, varp);
|
|
||||||
}
|
}
|
||||||
} else if (AstParamTypeDType* const ptp = VN_CAST(stmtp, ParamTypeDType)) {
|
} else if (AstParamTypeDType* const ptp = VN_CAST(stmtp, ParamTypeDType)) {
|
||||||
if (ptp->isGParam()) {
|
if (ptp->isGParam()) originalParamp = ptp->clonep();
|
||||||
const AstParamTypeDType* const oldptp = ptp->clonep();
|
|
||||||
clonemapp->emplace(oldptp, ptp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (originalIsCopy) originalParamp = m_originalParams[originalParamp];
|
||||||
|
clonemapp->emplace(originalParamp, stmtp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void relinkPins(const CloneMap* clonemapp, AstPin* startpinp) {
|
void relinkPins(const CloneMap* clonemapp, AstPin* startpinp) {
|
||||||
|
|
@ -545,9 +545,8 @@ class ParamProcessor final {
|
||||||
}
|
}
|
||||||
void replaceRefsRecurse(AstNode* const nodep, const AstClass* const oldClassp,
|
void replaceRefsRecurse(AstNode* const nodep, const AstClass* const oldClassp,
|
||||||
AstClass* const newClassp) {
|
AstClass* const newClassp) {
|
||||||
// Some of the nodes may be already marked as visited, because they were copied. They
|
// Self references linked in the first pass of V3LinkDot.cpp should point to the default
|
||||||
// should be marked as unvisited, because parameterized references have to be handled.
|
// instance.
|
||||||
nodep->user5(false);
|
|
||||||
if (AstClassRefDType* const classRefp = VN_CAST(nodep, ClassRefDType)) {
|
if (AstClassRefDType* const classRefp = VN_CAST(nodep, ClassRefDType)) {
|
||||||
if (classRefp->classp() == oldClassp) classRefp->classp(newClassp);
|
if (classRefp->classp() == oldClassp) classRefp->classp(newClassp);
|
||||||
} else if (AstClassOrPackageRef* const classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
|
} else if (AstClassOrPackageRef* const classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
|
||||||
|
|
@ -565,7 +564,13 @@ class ParamProcessor final {
|
||||||
// Deep clone of new module
|
// Deep clone of new module
|
||||||
// Note all module internal variables will be re-linked to the new modules by clone
|
// Note all module internal variables will be re-linked to the new modules by clone
|
||||||
// However links outside the module (like on the upper cells) will not.
|
// However links outside the module (like on the upper cells) will not.
|
||||||
AstNodeModule* const newmodp = srcModp->cloneTree(false);
|
AstNodeModule* newmodp;
|
||||||
|
if (srcModp->user2p()) {
|
||||||
|
newmodp = VN_CAST(srcModp->user2p()->cloneTree(false), NodeModule);
|
||||||
|
} else {
|
||||||
|
newmodp = srcModp->cloneTree(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (AstClass* const newClassp = VN_CAST(newmodp, Class)) {
|
if (AstClass* const newClassp = VN_CAST(newmodp, Class)) {
|
||||||
newClassp->isParameterized(false);
|
newClassp->isParameterized(false);
|
||||||
replaceRefsRecurse(newmodp->stmtsp(), newClassp, VN_AS(srcModp, Class));
|
replaceRefsRecurse(newmodp->stmtsp(), newClassp, VN_AS(srcModp, Class));
|
||||||
|
|
@ -603,7 +608,7 @@ class ParamProcessor final {
|
||||||
// Grab all I/O so we can remap our pins later
|
// Grab all I/O so we can remap our pins later
|
||||||
// Note we allow multiple users of a parameterized model,
|
// Note we allow multiple users of a parameterized model,
|
||||||
// thus we need to stash this info.
|
// thus we need to stash this info.
|
||||||
collectPins(clonemapp, newmodp);
|
collectPins(clonemapp, newmodp, srcModp->user2p());
|
||||||
// Relink parameter vars to the new module
|
// Relink parameter vars to the new module
|
||||||
relinkPins(clonemapp, paramsp);
|
relinkPins(clonemapp, paramsp);
|
||||||
// Fix any interface references
|
// Fix any interface references
|
||||||
|
|
@ -787,6 +792,18 @@ class ParamProcessor final {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void storeOriginalParams(AstClass* const classp) {
|
||||||
|
for (AstNode* stmtp = classp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||||
|
AstNode* originalParamp = nullptr;
|
||||||
|
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
|
||||||
|
if (varp->isGParam()) originalParamp = varp->clonep();
|
||||||
|
} else if (AstParamTypeDType* const ptp = VN_CAST(stmtp, ParamTypeDType)) {
|
||||||
|
if (ptp->isGParam()) originalParamp = ptp->clonep();
|
||||||
|
}
|
||||||
|
if (originalParamp) m_originalParams[stmtp] = originalParamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool nodeDeparamCommon(AstNode* nodep, AstNodeModule*& srcModpr, AstPin* paramsp,
|
bool nodeDeparamCommon(AstNode* nodep, AstNodeModule*& srcModpr, AstPin* paramsp,
|
||||||
AstPin* pinsp, bool any_overrides) {
|
AstPin* pinsp, bool any_overrides) {
|
||||||
// Make sure constification worked
|
// Make sure constification worked
|
||||||
|
|
@ -809,9 +826,16 @@ class ParamProcessor final {
|
||||||
|
|
||||||
if (!any_overrides) {
|
if (!any_overrides) {
|
||||||
UINFO(8, "Cell parameters all match original values, skipping expansion.\n");
|
UINFO(8, "Cell parameters all match original values, skipping expansion.\n");
|
||||||
// Mark that the defeult instance is used.
|
// If it's the first use of the default instance, create a copy and store it in user2p.
|
||||||
// It will be checked only if srcModpr is a class.
|
// user2p will also be used to check if the default instance is used.
|
||||||
srcModpr->user2(true);
|
if (!srcModpr->user2p() && VN_IS(srcModpr, Class)) {
|
||||||
|
AstClass* classCopyp = VN_AS(srcModpr, Class)->cloneTree(false);
|
||||||
|
// It is a temporary copy of the original class node, stored in order to create
|
||||||
|
// another instances. It is needed only during class instantiation.
|
||||||
|
pushDeletep(classCopyp);
|
||||||
|
srcModpr->user2p(classCopyp);
|
||||||
|
storeOriginalParams(classCopyp);
|
||||||
|
}
|
||||||
} else if (AstNodeModule* const paramedModp
|
} else if (AstNodeModule* const paramedModp
|
||||||
= m_hierBlocks.findByParams(srcModpr->name(), paramsp, m_modp)) {
|
= m_hierBlocks.findByParams(srcModpr->name(), paramsp, m_modp)) {
|
||||||
paramedModp->dead(false);
|
paramedModp->dead(false);
|
||||||
|
|
@ -919,7 +943,11 @@ public:
|
||||||
class ParamVisitor final : public VNVisitor {
|
class ParamVisitor final : public VNVisitor {
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// AstNodeModule::user1 -> bool: already fixed level
|
// AstNodeModule::user1 -> bool: already fixed level
|
||||||
// AstClass::user2 -> bool: Referenced (value read only in parameterized classes)
|
// AstClass::user2p -> AstClass*: Unchanged copy of the parameterized class node.
|
||||||
|
// The class node may be modified according to parameter
|
||||||
|
// values and an unchanged copy is needed to instantiate
|
||||||
|
// classes with different parameters.
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
ParamProcessor m_processor; // De-parameterize a cell, build modules
|
ParamProcessor m_processor; // De-parameterize a cell, build modules
|
||||||
UnrollStateful m_unroller; // Loop unroller
|
UnrollStateful m_unroller; // Loop unroller
|
||||||
|
|
@ -1377,8 +1405,8 @@ public:
|
||||||
for (AstNodeModule* const modp : modps) netlistp->addModulesp(modp);
|
for (AstNodeModule* const modp : modps) netlistp->addModulesp(modp);
|
||||||
|
|
||||||
for (AstClass* const classp : m_paramClasses) {
|
for (AstClass* const classp : m_paramClasses) {
|
||||||
if (!classp->user2()) {
|
if (!classp->user2p()) {
|
||||||
// Unreferenced, so it can be removed
|
// The default value isn't referenced, so it can be removed
|
||||||
VL_DO_DANGLING(pushDeletep(classp->unlinkFrBack()), classp);
|
VL_DO_DANGLING(pushDeletep(classp->unlinkFrBack()), classp);
|
||||||
} else {
|
} else {
|
||||||
// Referenced. classp became a specialized class with the default
|
// Referenced. classp became a specialized class with the default
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Wilson Snyder. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
compile(
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
class Foo #(type T=bit);
|
||||||
|
int x = $bits(T);
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class Bar #(type S=int) extends Foo#(S);
|
||||||
|
endclass
|
||||||
|
|
||||||
|
typedef Bar#() bar_default_t;
|
||||||
|
|
||||||
|
class Baz;
|
||||||
|
Bar#(logic[7:0]) bar_string;
|
||||||
|
int bar_x;
|
||||||
|
function new;
|
||||||
|
bar_string = new;
|
||||||
|
bar_x = bar_string.x;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
|
typedef Baz baz_t;
|
||||||
|
|
||||||
|
module t;
|
||||||
|
initial begin
|
||||||
|
bar_default_t bar_default = new;
|
||||||
|
baz_t baz = new;
|
||||||
|
|
||||||
|
if (bar_default.x != 32) $stop;
|
||||||
|
if (baz.bar_x != 8) $stop;
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue