Support generic interface arrays (#7604)
This commit is contained in:
parent
0c9448dadd
commit
7664bbb3ef
|
|
@ -106,6 +106,13 @@ public:
|
|||
return const_cast<AstNodeDType*>(
|
||||
static_cast<const AstNodeDType*>(this)->skipRefIterp(false, false));
|
||||
}
|
||||
// If array, returns element dtype, otherwise returns skipRef dtype
|
||||
// If skipRef is false, RefDTypes are not followed (safe before typedef linking)
|
||||
const AstNodeDType* elemDTypep(bool skipRef = true) const VL_MT_STABLE;
|
||||
AstNodeDType* elemDTypep(bool skipRef = true) VL_MT_STABLE {
|
||||
return const_cast<AstNodeDType*>(
|
||||
static_cast<const AstNodeDType*>(this)->elemDTypep(skipRef));
|
||||
}
|
||||
// (Slow) recurses - Structure alignment 1,2,4 or 8 bytes (arrays affect this)
|
||||
virtual int widthAlignBytes() const = 0;
|
||||
// (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,...
|
||||
|
|
|
|||
|
|
@ -1050,6 +1050,25 @@ const AstNodeDType* AstNodeDType::skipRefIterp(bool skipConst, bool skipEnum,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const AstNodeDType* AstNodeDType::elemDTypep(bool skipRef) const {
|
||||
const AstNodeDType* dtypep = this;
|
||||
while (true) {
|
||||
if (skipRef) dtypep = dtypep->skipRefp();
|
||||
if (const AstBracketArrayDType* const adtypep = VN_CAST(dtypep, BracketArrayDType)) {
|
||||
dtypep = adtypep->subDTypep();
|
||||
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
dtypep = adtypep->subDTypep();
|
||||
} else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
dtypep = adtypep->subDTypep();
|
||||
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
dtypep = adtypep->subDTypep();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dtypep;
|
||||
}
|
||||
|
||||
bool AstNodeDType::similarDType(const AstNodeDType* samep) const {
|
||||
const AstNodeDType* nodep = this;
|
||||
nodep = nodep->skipRefToNonRefp();
|
||||
|
|
|
|||
|
|
@ -704,6 +704,19 @@ private:
|
|||
for (size_t i = 0; i < indices.size(); ++i) {
|
||||
indexStr += "__BRA__" + AstNode::encodeNumber(indices[i] + arrs[i]->lo()) + "__KET__";
|
||||
}
|
||||
AstMemberSel* const parentSelp = VN_CAST(nodep->backp(), MemberSel);
|
||||
if (parentSelp && parentSelp->fromp() == nodep && parentSelp->varp()) {
|
||||
AstVar* const memberVarp = parentSelp->varp();
|
||||
AstVarXRef* const newp
|
||||
= new AstVarXRef{parentSelp->fileline(), memberVarp->name(),
|
||||
varrefp->name() + indexStr, parentSelp->access()};
|
||||
newp->varp(memberVarp);
|
||||
newp->dtypep(parentSelp->dtypep());
|
||||
newp->classOrPackagep(varrefp->classOrPackagep());
|
||||
parentSelp->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(parentSelp), parentSelp);
|
||||
return;
|
||||
}
|
||||
AstVarXRef* const newp
|
||||
= new AstVarXRef{nodep->fileline(), varrefp->name() + indexStr, "", VAccess::READ};
|
||||
newp->dtypep(irp);
|
||||
|
|
|
|||
|
|
@ -3553,24 +3553,6 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
m_ds.init(m_curSymp);
|
||||
iterateNull(nodep);
|
||||
}
|
||||
static const AstNodeDType* getElemDTypep(const AstNodeDType* dtypep) {
|
||||
dtypep = dtypep->skipRefp();
|
||||
while (true) {
|
||||
if (const AstBracketArrayDType* const adtypep = VN_CAST(dtypep, BracketArrayDType)) {
|
||||
dtypep = adtypep->subDTypep()->skipRefp();
|
||||
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
dtypep = adtypep->subDTypep()->skipRefp();
|
||||
} else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
dtypep = adtypep->subDTypep()->skipRefp();
|
||||
} else if (const AstUnpackArrayDType* const adtypep
|
||||
= VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
dtypep = adtypep->subDTypep()->skipRefp();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dtypep;
|
||||
}
|
||||
static const AstNodeDType* getExprDTypep(const AstNodeExpr* selp) {
|
||||
while (const AstNodePreSel* const sp = VN_CAST(selp, NodePreSel)) selp = sp->fromp();
|
||||
if (const AstMemberSel* const sp = VN_CAST(selp, MemberSel)) {
|
||||
|
|
@ -3582,7 +3564,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
dtypep = nodep->childDTypep();
|
||||
return nodep->name() == name;
|
||||
});
|
||||
if (found) return getElemDTypep(dtypep);
|
||||
if (found) return dtypep->elemDTypep();
|
||||
selp->v3error("Class " << classRefp->prettyNameQ()
|
||||
<< " does not contain field " << selp->prettyNameQ());
|
||||
} else {
|
||||
|
|
@ -3592,7 +3574,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
} else if (const AstNodeVarRef* const varRefp = VN_CAST(selp, NodeVarRef)) {
|
||||
return getElemDTypep(varRefp->varp()->childDTypep());
|
||||
return varRefp->varp()->childDTypep()->elemDTypep();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -3617,7 +3599,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
pinp = VN_CAST(pinp->nextp(), Pin),
|
||||
modIfaceVarp = getNextVarp(modIfaceVarp->nextp())) {
|
||||
if (modIfaceVarp->varType() != VVarType::IFACEREF
|
||||
|| !VN_IS(modIfaceVarp->childDTypep(), IfaceGenericDType)) {
|
||||
|| !VN_IS(modIfaceVarp->childDTypep()->elemDTypep(), IfaceGenericDType)) {
|
||||
continue;
|
||||
}
|
||||
AstNode* exprp = pinp->exprp();
|
||||
|
|
@ -3662,7 +3644,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
} else if (varRefp) {
|
||||
const AstVar* const varp = varRefp->varp();
|
||||
if (const AstIfaceRefDType* const refp
|
||||
= VN_CAST(getElemDTypep(varp->childDTypep()), IfaceRefDType)) {
|
||||
= VN_CAST(varp->childDTypep()->elemDTypep(), IfaceRefDType)) {
|
||||
AstIface* const ifacep = VN_AS(refp->cellp()->modp(), Iface);
|
||||
AstIfaceRefDType* newIfaceRefp;
|
||||
if (refp->modportp()) {
|
||||
|
|
|
|||
|
|
@ -460,7 +460,8 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
m_moduleWithGenericIface |= VN_IS(nodep->childDTypep(), IfaceGenericDType);
|
||||
const AstNodeDType* const dtypep = nodep->childDTypep();
|
||||
m_moduleWithGenericIface |= dtypep && VN_IS(dtypep->elemDTypep(false), IfaceGenericDType);
|
||||
|
||||
// Maybe this variable has a signal attribute
|
||||
V3Control::applyVarAttr(m_modp, m_ftaskp, nodep);
|
||||
|
|
|
|||
|
|
@ -1710,7 +1710,9 @@ class ParamProcessor final {
|
|||
bool& any_overridesr, IfaceRefRefs& ifaceRefRefs) {
|
||||
for (AstPin* pinp = pinsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
|
||||
const AstVar* const modvarp = pinp->modVarp();
|
||||
if (modvarp && VN_IS(modvarp->subDTypep(), IfaceGenericDType)) continue;
|
||||
if (modvarp && VN_IS(arraySubDTypeDeepp(modvarp->subDTypep()), IfaceGenericDType)) {
|
||||
continue;
|
||||
}
|
||||
if (modvarp->isIfaceRef()) {
|
||||
// arraySubDTypeDeepp returns input unchanged if not an array.
|
||||
AstIfaceRefDType* const portIrefp
|
||||
|
|
@ -1798,10 +1800,12 @@ class ParamProcessor final {
|
|||
for (const AstNode* nodep = pinsp; nodep; nodep = nodep->nextp()) {
|
||||
if (const AstPin* const pinp = VN_CAST(nodep, Pin)) {
|
||||
if (AstVar* const varp = pinp->modVarp()) {
|
||||
AstNodeDType* dtypep = varp->childDTypep()->elemDTypep();
|
||||
if (AstIfaceGenericDType* const ifaceGDTypep
|
||||
= VN_CAST(varp->childDTypep(), IfaceGenericDType)) {
|
||||
= VN_CAST(dtypep, IfaceGenericDType)) {
|
||||
const auto iter = paramspMap.find(varp->name());
|
||||
if (iter == paramspMap.end()) continue;
|
||||
AstNode* const backp = ifaceGDTypep->backp();
|
||||
ifaceGDTypep->unlinkFrBack();
|
||||
const AstPin* const paramp = iter->second;
|
||||
paramspMap.erase(iter);
|
||||
|
|
@ -1812,7 +1816,13 @@ class ParamProcessor final {
|
|||
ifaceGDTypep->name(), ifacerefp->ifaceName(),
|
||||
ifaceGDTypep->modportName()};
|
||||
newIfacerefp->ifacep(ifacerefp->ifacep());
|
||||
varp->childDTypep(newIfacerefp);
|
||||
if (auto* const arrDtp = VN_CAST(backp, NodeArrayDType)) {
|
||||
arrDtp->childDTypep(newIfacerefp);
|
||||
} else if (auto* const arrDtp = VN_CAST(backp, BracketArrayDType)) {
|
||||
arrDtp->childDTypep(newIfacerefp);
|
||||
} else {
|
||||
varp->childDTypep(newIfacerefp);
|
||||
}
|
||||
VL_DO_DANGLING(m_deleter.pushDeletep(ifaceGDTypep), ifaceGDTypep);
|
||||
if (paramspMap.empty()) return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -338,7 +338,8 @@ public:
|
|||
m_varDeclTyped = true;
|
||||
const std::string uniqueName = "__VGIfaceParam" + nodep->name();
|
||||
AstNode::addNext(nodep,
|
||||
createVariable(nodep->fileline(), uniqueName, rangep, sigAttrListp));
|
||||
createVariable(nodep->fileline(), uniqueName, nullptr,
|
||||
sigAttrListp ? sigAttrListp->cloneTree(true) : nullptr));
|
||||
m_varDecl = VVarType::IFACEREF;
|
||||
AstIfaceGenericDType* const refdtypep
|
||||
= new AstIfaceGenericDType{nodep->fileline(), modportFileline, modportstrp};
|
||||
|
|
|
|||
|
|
@ -1033,8 +1033,10 @@ class WidthVisitor final : public VNVisitor {
|
|||
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
|
||||
const AstNode* basep = nodep->backp();
|
||||
while (VN_IS(basep, Range)) basep = basep->backp();
|
||||
if (nodep->ascending() && !VN_IS(basep, UnpackArrayDType)
|
||||
&& !VN_IS(basep, 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: ["
|
||||
|
|
|
|||
|
|
@ -1385,9 +1385,9 @@ port<nodep>: // ==IEEE: port
|
|||
AstNodeDType* const dtp = new AstIfaceRefDType{$<fl>2, $<fl>4, "", *$2, *$4};
|
||||
VARDTYPE(dtp); VARIOANSI();
|
||||
addNextNull($$, VARDONEP($$, $6, $7)); }
|
||||
| portDirNetE yINTERFACE portSig rangeListE sigAttrListE
|
||||
| portDirNetE yINTERFACE portSig variable_dimensionListE sigAttrListE
|
||||
{ $$ = $3; GRAMMARP->createGenericIface($3, $4, $5); }
|
||||
| portDirNetE yINTERFACE '.' idAny/*modport*/ portSig rangeListE sigAttrListE
|
||||
| portDirNetE yINTERFACE '.' idAny/*modport*/ portSig variable_dimensionListE sigAttrListE
|
||||
{ $$ = $5; GRAMMARP->createGenericIface($5, $6, $7, $<fl>4, *$4); }
|
||||
//
|
||||
| portDirNetE yINTERCONNECT signingE rangeListE portSig variable_dimensionListE sigAttrListE
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['--timing'])
|
||||
test.compile(verilator_flags2=['--binary'])
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ module GenericModule (interface a, interface b);
|
|||
initial begin
|
||||
#1;
|
||||
if (a.v != 7) $stop;
|
||||
if (b.k != 9) $stop;
|
||||
b.k = 9;
|
||||
end
|
||||
endmodule
|
||||
|
||||
|
|
@ -26,7 +26,8 @@ module t;
|
|||
GenericModule genericModule (inf_inst, inf_inst2);
|
||||
initial begin
|
||||
inf_inst.v = 7;
|
||||
inf_inst2.k = 9;
|
||||
#2;
|
||||
if (inf_inst2.k != 9) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['--timing'])
|
||||
test.compile(verilator_flags2=['--binary'])
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ module GenericModule (interface a);
|
|||
initial begin
|
||||
#1;
|
||||
if (a.v != 7) $stop;
|
||||
a.v = 9;
|
||||
end
|
||||
endmodule
|
||||
|
||||
|
|
@ -20,6 +21,8 @@ module t;
|
|||
GenericModule genericModule (inf_inst[2]);
|
||||
initial begin
|
||||
inf_inst[2].v = 7;
|
||||
#2;
|
||||
if (inf_inst[2].v != 9) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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,65 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface inf;
|
||||
int v;
|
||||
endinterface
|
||||
|
||||
module GenericModule1D (interface a[4]);
|
||||
initial begin
|
||||
#1;
|
||||
if (a[0].v != 'hdead) $stop;
|
||||
if (a[1].v != 'hbeef) $stop;
|
||||
a[2].v = 'hface;
|
||||
a[3].v = 'hcafe;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module GenericModule2D (interface a[2][2]);
|
||||
initial begin
|
||||
#3;
|
||||
if (a[0][0].v != 'hdead) $stop;
|
||||
a[0][1].v = 'hbeef;
|
||||
if (a[1][0].v != 'hface) $stop;
|
||||
a[1][1].v = 'hcafe;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module GenericModuleRng (interface a[5:3]);
|
||||
initial begin
|
||||
#5;
|
||||
if (a[3].v != 'hdead) $stop;
|
||||
if (a[4].v != 'hbeef) $stop;
|
||||
a[5].v = 'hface;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
inf inf1d[4]();
|
||||
inf inf2d[2][2]();
|
||||
inf infrng[5:3]();
|
||||
GenericModule1D mod1d(inf1d);
|
||||
GenericModule2D mod2d(inf2d);
|
||||
GenericModuleRng modrng(infrng);
|
||||
initial begin
|
||||
inf1d[0].v = 'hdead;
|
||||
inf1d[1].v = 'hbeef;
|
||||
#2;
|
||||
if (inf1d[2].v != 'hface) $stop;
|
||||
if (inf1d[3].v != 'hcafe) $stop;
|
||||
inf2d[0][0].v = 'hdead;
|
||||
inf2d[1][0].v = 'hface;
|
||||
#2;
|
||||
if (inf2d[0][1].v != 'hbeef) $stop;
|
||||
if (inf2d[1][1].v != 'hcafe) $stop;
|
||||
infrng[3].v = 'hdead;
|
||||
infrng[4].v = 'hbeef;
|
||||
#2;
|
||||
if (infrng[5].v != 'hface) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue