Support generic interface arrays (#7604)

This commit is contained in:
Krzysztof Bieganski 2026-06-03 04:28:50 +02:00 committed by GitHub
parent 0c9448dadd
commit 7664bbb3ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 157 additions and 35 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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: ["

View File

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

View File

@ -11,7 +11,7 @@ import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=['--timing'])
test.compile(verilator_flags2=['--binary'])
test.execute()

View File

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

View File

@ -11,7 +11,7 @@ import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=['--timing'])
test.compile(verilator_flags2=['--binary'])
test.execute()

View File

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

View File

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

View File

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