Support virtual interfaces (#3654)

This commit is contained in:
Arkadiusz Kozdra 2022-10-20 12:31:00 +02:00 committed by GitHub
parent e6add5e0b8
commit 0e4da3b0bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 440 additions and 45 deletions

View File

@ -768,6 +768,7 @@ public:
};
class AstIfaceRefDType final : public AstNodeDType {
// Reference to an interface, either for a port, or inside parent cell
// @astgen op1 := paramsp : List[AstPin]
private:
FileLine* m_modportFileline; // Where modport token was
string m_cellName; // "" = no cell, such as when connects to 'input' iface
@ -790,6 +791,14 @@ public:
, m_cellName{cellName}
, m_ifaceName{ifaceName}
, m_modportName{modport} {}
AstIfaceRefDType(FileLine* fl, FileLine* modportFl, const string& cellName,
const string& ifaceName, const string& modport, AstPin* paramsp)
: ASTGEN_SUPER_IfaceRefDType(fl)
, m_modportFileline{modportFl}
, m_cellName{cellName}
, m_ifaceName{ifaceName} {
addParamsp(paramsp);
}
ASTGEN_MEMBERS_AstIfaceRefDType;
// METHODS
const char* broken() const override;

View File

@ -2002,6 +2002,7 @@ class AstVar final : public AstNode {
bool m_usedClock : 1; // Signal used as a clock
bool m_usedParam : 1; // Parameter is referenced (on link; later signals not setup)
bool m_usedLoopIdx : 1; // Variable subject of for unrolling
bool m_usedVirtIface : 1; // Signal used through a virtual interface
bool m_funcLocal : 1; // Local variable for a function
bool m_funcReturn : 1; // Return variable for a function
bool m_attrScBv : 1; // User force bit vector attribute
@ -2040,6 +2041,7 @@ class AstVar final : public AstNode {
m_usedClock = false;
m_usedParam = false;
m_usedLoopIdx = false;
m_usedVirtIface = false;
m_sigPublic = false;
m_sigModPublic = false;
m_sigUserRdPublic = false;
@ -2188,6 +2190,7 @@ public:
void usedClock(bool flag) { m_usedClock = flag; }
void usedParam(bool flag) { m_usedParam = flag; }
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
void usedVirtIface(bool flag) { m_usedVirtIface = flag; }
void sigPublic(bool flag) { m_sigPublic = flag; }
void sigModPublic(bool flag) { m_sigModPublic = flag; }
void sigUserRdPublic(bool flag) {
@ -2271,6 +2274,7 @@ public:
bool isUsedClock() const { return m_usedClock; }
bool isUsedParam() const { return m_usedParam; }
bool isUsedLoopIdx() const { return m_usedLoopIdx; }
bool isUsedVirtIface() const { return m_usedVirtIface; }
bool isSc() const VL_MT_SAFE { return m_sc; }
bool isScQuad() const;
bool isScBv() const;

View File

@ -724,6 +724,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
info.m_type += ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
info.m_type = EmitCBaseVisitor::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
} else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
if (adtypep->isCompound()) compound = true;
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound);

View File

@ -47,6 +47,19 @@ static void makeVlToString(AstClass* nodep) {
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtsp(funcp);
}
static void makeVlToString(AstIface* nodep) {
AstCFunc* const funcp
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
funcp->argTypes("const " + EmitCBaseVisitor::prefixNameProtect(nodep) + "* obj");
funcp->isMethod(false);
funcp->isConst(false);
funcp->isStatic(false);
funcp->protect(false);
AstNode* const exprp = new AstCMath{nodep->fileline(), "obj ? obj->name() : \"null\"", 0};
exprp->dtypeSetString();
funcp->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
nodep->addStmtsp(funcp);
}
static void makeToString(AstClass* nodep) {
AstCFunc* const funcp = new AstCFunc{nodep->fileline(), "to_string", nullptr, "std::string"};
funcp->isConst(true);
@ -117,6 +130,8 @@ void V3Common::commonAll() {
makeVlToString(classp);
makeToString(classp);
makeToStringMiddle(classp);
} else if (AstIface* const ifacep = VN_CAST(nodep, Iface)) {
makeVlToString(ifacep);
}
}
V3Global::dumpCheckGlobalTree("common", 0, dumpTree() >= 3);

View File

@ -2606,7 +2606,7 @@ private:
&& m_doNConst
&& v3Global.opt.fConst()
// Default value, not a "known" constant for this usage
&& !nodep->varp()->isClassMember()
&& !nodep->varp()->isClassMember() && !nodep->varp()->isUsedVirtIface()
&& !(nodep->varp()->isFuncLocal() && nodep->varp()->isNonOutput())
&& !nodep->varp()->noSubst() && !nodep->varp()->isSigPublic())
|| nodep->varp()->isParam())) {

View File

@ -198,6 +198,19 @@ private:
}
if (nodep->classp()) nodep->classp()->user1Inc();
}
void visit(AstIfaceRefDType* nodep) override {
iterateChildren(nodep);
checkDType(nodep);
checkAll(nodep);
if (nodep->modportp()) {
if (m_elimCells) {
nodep->modportp(nullptr);
} else {
nodep->modportp()->user1Inc();
}
}
if (nodep->ifaceViaCellp()) nodep->ifaceViaCellp()->user1Inc();
}
void visit(AstNodeDType* nodep) override {
iterateChildren(nodep);
checkDType(nodep);
@ -321,7 +334,7 @@ private:
}
bool mightElimVar(AstVar* nodep) const {
if (nodep->isSigPublic()) return false; // Can't elim publics!
if (nodep->isIO() || nodep->isClassMember()) return false;
if (nodep->isIO() || nodep->isClassMember() || nodep->isUsedVirtIface()) return false;
if (nodep->isTemp() && !nodep->isTrace()) return true;
return m_elimUserVars; // Post-Trace can kill most anything
}

View File

@ -663,6 +663,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
suffix + ".atDefault()" + cvtarray);
} else if (VN_IS(dtypep, ClassRefDType)) {
return ""; // Constructor does it
} else if (VN_IS(dtypep, IfaceRefDType)) {
return varNameProtected + suffix + " = nullptr;\n";
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
// Access std::array as C array
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");

View File

@ -1145,6 +1145,9 @@ public:
} else if (VN_IS(varModp, Class) && varModp != m_modp) {
// Superclass member reference
puts(prefixNameProtect(varModp) + "::");
} else if (varp->isIfaceRef()) {
puts(nodep->selfPointerProtect(m_useSelfForThis));
return;
} else if (!nodep->selfPointer().empty()) {
emitDereference(nodep->selfPointerProtect(m_useSelfForThis));
}

View File

@ -141,7 +141,7 @@ public:
void checkRemoveAssign(const LifeMap::iterator& it) {
const AstVar* const varp = it->first->varp();
LifeVarEntry* const entp = &(it->second);
if (!varp->isSigPublic()) {
if (!varp->isSigPublic() && !varp->isUsedVirtIface()) {
// Rather than track what sigs AstUCFunc/AstUCStmt may change,
// we just don't optimize any public sigs
// Check the var entry, and remove if appropriate
@ -186,7 +186,7 @@ public:
const auto it = m_map.find(nodep);
if (it != m_map.end()) {
if (AstConst* const constp = it->second.constNodep()) {
if (!varrefp->varp()->isSigPublic()) {
if (!varrefp->varp()->isSigPublic() && !varrefp->varp()->isUsedVirtIface()) {
// Aha, variable is constant; substitute in.
// We'll later constant propagate
UINFO(4, " replaceconst: " << varrefp << endl);

View File

@ -459,7 +459,7 @@ public:
}
void computeIfaceVarSyms() {
for (VSymEnt* varSymp : m_ifaceVarSyms) {
const AstVar* const varp = varSymp ? VN_AS(varSymp->nodep(), Var) : nullptr;
AstVar* const varp = varSymp ? VN_AS(varSymp->nodep(), Var) : nullptr;
UINFO(9, " insAllIface se" << cvtToHex(varSymp) << " " << varp << endl);
AstIfaceRefDType* const ifacerefp = ifaceRefFromArray(varp->subDTypep());
UASSERT_OBJ(ifacerefp, varp, "Non-ifacerefs on list!");
@ -475,8 +475,16 @@ public:
ifacerefp->v3fatalSrc("Unlinked interface");
}
} else if (ifacerefp->ifaceViaCellp()->dead()) {
ifacerefp->v3error("Parent instance's interface is not found: "
<< AstNode::prettyNameQ(ifacerefp->ifaceName()));
if (varp->isIfaceRef()) {
ifacerefp->v3error("Parent instance's interface is not found: "
<< AstNode::prettyNameQ(ifacerefp->ifaceName()));
} else {
ifacerefp->v3warn(
E_UNSUPPORTED,
"Unsupported: virtual interface never assigned any actual interface");
varp->dtypep(ifacerefp->findCHandleDType());
VL_DO_DANGLING(ifacerefp->unlinkFrBack()->deleteTree(), ifacerefp);
}
continue;
}
VSymEnt* const ifaceSymp = getNodeSym(ifacerefp->ifaceViaCellp());
@ -2420,7 +2428,7 @@ private:
m_ds.m_dotSymp = foundp;
m_ds.m_dotPos = DP_SCOPE;
// Upper AstDot visitor will handle it from here
} else if (VN_IS(foundp->nodep(), Cell) && allowVar && m_cellp) {
} else if (VN_IS(foundp->nodep(), Cell) && allowVar) {
AstCell* const cellp = VN_AS(foundp->nodep(), Cell);
if (VN_IS(cellp->modp(), Iface)) {
// Interfaces can be referenced like a variable for interconnect
@ -2438,7 +2446,7 @@ private:
m_ds.m_dotPos = DP_SCOPE;
UINFO(9, " cell -> iface varref " << foundp->nodep() << endl);
AstNode* const newp
= new AstVarRef(ifaceRefVarp->fileline(), ifaceRefVarp, VAccess::READ);
= new AstVarRef{nodep->fileline(), ifaceRefVarp, VAccess::READ};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else if (VN_IS(cellp->modp(), NotFoundModule)) {
@ -2449,7 +2457,7 @@ private:
} else if (AstVar* const varp = foundToVarp(foundp, nodep, VAccess::READ)) {
AstIfaceRefDType* const ifacerefp
= LinkDotState::ifaceRefFromArray(varp->subDTypep());
if (ifacerefp) {
if (ifacerefp && varp->isIfaceRef()) {
UASSERT_OBJ(ifacerefp->ifaceViaCellp(), ifacerefp, "Unlinked interface");
// Really this is a scope reference into an interface
UINFO(9, "varref-ifaceref " << m_ds.m_dotText << " " << nodep << endl);
@ -2538,9 +2546,9 @@ private:
m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name());
m_ds.m_dotSymp = foundp;
m_ds.m_dotPos = DP_SCOPE;
UINFO(9, " cell -> iface varref " << foundp->nodep() << endl);
AstNode* newp
= new AstVarRef(ifaceRefVarp->fileline(), ifaceRefVarp, VAccess::READ);
UINFO(9, " modport -> iface varref " << foundp->nodep() << endl);
// We lose the modport name here, so we cannot detect mismatched modports.
AstNode* newp = new AstVarRef{nodep->fileline(), ifaceRefVarp, VAccess::READ};
auto* const cellarrayrefp = VN_CAST(m_ds.m_unlinkedScopep, CellArrayRef);
if (cellarrayrefp) {
// iface[vec].modport became CellArrayRef(iface, lsb)

View File

@ -169,6 +169,7 @@ private:
&& !nodep->varp()->isFuncLocal() // Not already a function local (e.g.: argument)
&& !nodep->varp()->isStatic() // Not a static variable
&& !nodep->varp()->isClassMember() // Statically exists in design hierarchy
&& !nodep->varp()->isUsedVirtIface() // Not used through a virtual interface
&& !nodep->varp()->valuep() // Does not have an initializer
) {
UINFO(4, "Consider for localization: " << nodep << endl);

View File

@ -43,6 +43,7 @@ class ScopeVisitor final : public VNVisitor {
private:
// NODE STATE
// AstVar::user1p -> AstVarScope replacement for this variable
// AstCell::user2p -> AstScope*. The scope created inside the cell
// AstTask::user2p -> AstTask*. Replacement task
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
@ -125,6 +126,10 @@ private:
AstNodeModule* const modp = cellp->modp();
UASSERT_OBJ(modp, cellp, "Unlinked mod");
iterate(modp); // Recursive call to visit(AstNodeModule)
if (VN_IS(modp, Iface)) {
// Remember newly created scope
cellp->user2p(m_scopep);
}
}
}
}
@ -261,7 +266,12 @@ private:
void visit(AstVar* nodep) override {
// Make new scope variable
if (!nodep->user1p()) {
AstVarScope* const varscp = new AstVarScope(nodep->fileline(), m_scopep, nodep);
AstScope* scopep = m_scopep;
if (AstIfaceRefDType* const ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) {
// Attach every non-virtual interface variable its inner scope
if (ifacerefp->cellp()) scopep = VN_AS(ifacerefp->cellp()->user2p(), Scope);
}
AstVarScope* const varscp = new AstVarScope{nodep->fileline(), scopep, nodep};
UINFO(6, " New scope " << varscp << endl);
if (m_aboveCellp && !m_aboveCellp->isTrace()) varscp->trace(false);
nodep->user1p(varscp);
@ -280,15 +290,11 @@ private:
// VarRef needs to point to VarScope
// Make sure variable has made user1p.
UASSERT_OBJ(nodep->varp(), nodep, "Unlinked");
if (nodep->varp()->isIfaceRef()) {
nodep->varScopep(nullptr);
} else {
// We may have not made the variable yet, and we can't make it now as
// the var's referenced package etc might not be created yet.
// So push to a list and post-correct.
// No check here for nodep->classOrPackagep(), will check when walk list.
m_varRefScopes.emplace(nodep, m_scopep);
}
// We may have not made the variable yet, and we can't make it now as
// the var's referenced package etc might not be created yet.
// So push to a list and post-correct.
// No check here for nodep->classOrPackagep(), will check when walk list.
m_varRefScopes.emplace(nodep, m_scopep);
}
void visit(AstScopeName* nodep) override {
// If there's a %m in the display text, we add a special node that will contain the name()

View File

@ -2527,6 +2527,20 @@ private:
<< foundp->warnOther() << "... Location of found object\n"
<< foundp->warnContextSecondary());
}
} else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
if (AstNode* const foundp = memberSelIface(nodep, adtypep)) {
if (AstVar* const varp = VN_CAST(foundp, Var)) {
nodep->dtypep(foundp->dtypep());
nodep->varp(varp);
varp->usedVirtIface(true);
return;
}
UINFO(1, "found object " << foundp << endl);
nodep->v3fatalSrc("MemberSel of non-variable\n"
<< nodep->warnContextPrimary() << '\n'
<< foundp->warnOther() << "... Location of found object\n"
<< foundp->warnContextSecondary());
}
} else if (VN_IS(fromDtp, EnumDType) //
|| VN_IS(fromDtp, AssocArrayDType) //
|| VN_IS(fromDtp, WildcardArrayDType) //
@ -2576,6 +2590,26 @@ private:
<< (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
return nullptr; // Caller handles error
}
AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) {
// Returns node if ok
// No need to width-resolve the interface, as it was done when we did the child
AstNodeModule* const ifacep = adtypep->ifacep();
UASSERT_OBJ(ifacep, nodep, "Unlinked");
// if (AstNode* const foundp = ifacep->findMember(nodep->name())) return foundp;
VSpellCheck speller;
for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) {
if (itemp->name() == nodep->name()) return itemp;
if (VN_IS(itemp, Var) || VN_IS(itemp, Modport)) {
speller.pushCandidate(itemp->prettyName());
}
}
const string suggest = speller.bestCandidateMsg(nodep->prettyName());
nodep->v3error(
"Member " << nodep->prettyNameQ() << " not found in interface "
<< ifacep->prettyNameQ() << "\n"
<< (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
return nullptr; // Caller handles error
}
bool memberSelStruct(AstMemberSel* nodep, AstNodeUOrStructDType* adtypep) {
// Returns true if ok
if (AstMemberDType* const memberp = adtypep->findMember(nodep->name())) {
@ -6026,11 +6060,38 @@ private:
// Note the check uses the expected size, not the child's subDTypep as we want the
// child node's width to end up correct for the assignment (etc)
widthCheckSized(nodep, side, underp, expDTypep, extendRule, warnOn);
} else if (!VN_IS(expDTypep, IfaceRefDType)
&& VN_IS(underp->dtypep(), IfaceRefDType)) {
} else if (!VN_IS(expDTypep->skipRefp(), IfaceRefDType)
&& VN_IS(underp->dtypep()->skipRefp(), IfaceRefDType)) {
underp->v3error(ucfirst(nodep->prettyOperatorName())
<< " expected non-interface on " << side << " but '"
<< underp->name() << "' is an interface.");
} else if (const AstIfaceRefDType* expIfaceRefp
= VN_CAST(expDTypep->skipRefp(), IfaceRefDType)) {
const AstIfaceRefDType* underIfaceRefp
= VN_CAST(underp->dtypep()->skipRefp(), IfaceRefDType);
if (!underIfaceRefp) {
underp->v3error(ucfirst(nodep->prettyOperatorName())
<< " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ()
<< " interface on " << side << " but " << underp->prettyNameQ()
<< " is not an interface.");
} else if (expIfaceRefp->ifaceViaCellp() != underIfaceRefp->ifaceViaCellp()) {
underp->v3error(ucfirst(nodep->prettyOperatorName())
<< " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ()
<< " interface on " << side << " but '" << underp->name()
<< "' is a different interface ("
<< underIfaceRefp->ifaceViaCellp()->prettyNameQ() << ").");
} else if (underIfaceRefp->modportp()
&& expIfaceRefp->modportp() != underIfaceRefp->modportp()) {
underp->v3error(ucfirst(nodep->prettyOperatorName())
<< " expected "
<< (expIfaceRefp->modportp()
? expIfaceRefp->modportp()->prettyNameQ()
: "no")
<< " interface modport on " << side << " but got "
<< underIfaceRefp->modportp()->prettyNameQ() << " modport.");
} else {
underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
}
} else {
// Hope it just works out (perhaps a cast will deal with it)
underp = userIterateSubtreeReturnEdits(underp, WidthVP(expDTypep, FINAL).p());

View File

@ -1402,9 +1402,9 @@ port<nodep>: // ==IEEE: port
VARDTYPE(new AstIfaceRefDType($<fl>2, $<fl>4, "", *$2, *$4));
addNextNull($$, VARDONEP($$,$6,$7)); }
| portDirNetE yINTERFACE portSig rangeListE sigAttrListE
{ $$ = nullptr; BBUNSUP($<fl>2, "Unsupported: virtual or generic interfaces"); }
{ $$ = nullptr; BBUNSUP($<fl>2, "Unsupported: generic interfaces"); }
| portDirNetE yINTERFACE '.' idAny/*modport*/ portSig rangeListE sigAttrListE
{ $$ = nullptr; BBUNSUP($<fl>2, "Unsupported: virtual or generic interfaces"); }
{ $$ = nullptr; BBUNSUP($<fl>2, "Unsupported: generic interfaces"); }
//
// // IEEE: ansi_port_declaration, with [port_direction] removed
// // IEEE: [ net_port_header | interface_port_header ]
@ -1981,21 +1981,25 @@ data_typeNoRef<nodeDTypep>: // ==IEEE: data_type, excluding class_ty
{ $$ = new AstBasicDType{$1, VBasicDTypeKwd::CHANDLE}; }
| yEVENT
{ $$ = new AstBasicDType{$1, VBasicDTypeKwd::EVENT}; v3Global.setHasEvents(); }
// // Rules overlap virtual_interface_declaration
// // Parameters here are SV2009
// // IEEE has ['.' modport] but that will conflict with port
// // declarations which decode '.' modport themselves, so
// // instead see data_typeVar
| yVIRTUAL__INTERFACE yINTERFACE id/*interface*/
{ $$ = new AstBasicDType{$1, VBasicDTypeKwd::CHANDLE};
BBUNSUP($1, "Unsupported: virtual interface"); }
| yVIRTUAL__anyID id/*interface*/
{ $$ = new AstBasicDType{$1, VBasicDTypeKwd::CHANDLE};
BBUNSUP($1, "Unsupported: virtual data type"); }
| type_reference { $$ = $1; }
// // IEEE: class_scope: see data_type above
// // IEEE: class_type: see data_type above
// // IEEE: ps_covergroup: see data_type above
// // Rules overlap virtual_interface_declaration
| yVIRTUAL__INTERFACE yINTERFACE data_typeVirtual
{ $$ = $3; }
| yVIRTUAL__anyID data_typeVirtual
{ $$ = $2; }
;
data_typeVirtual<nodeDTypep>: // ==IEEE: data_type after yVIRTUAL [ yINTERFACE ]
// // Parameters here are SV2009
id/*interface*/ { $$ = new AstIfaceRefDType{$<fl>1, "", *$1}; }
| id/*interface*/ '.' id/*modport*/ { $$ = new AstIfaceRefDType{$<fl>1, $<fl>3, "", *$1, *$3}; }
| id/*interface*/ parameter_value_assignmentClass
{ $$ = new AstIfaceRefDType{$<fl>1, nullptr, "", *$1, "", $2}; }
| id/*interface*/ parameter_value_assignmentClass '.' id/*modport*/
{ $$ = new AstIfaceRefDType{$<fl>1, $<fl>4, "", *$1, *$4, $2}; }
;
data_type_or_void<nodeDTypep>: // ==IEEE: data_type_or_void

View File

@ -1,10 +1,3 @@
%Error-UNSUPPORTED: t/t_class_unsup_bad.v:7:1: Unsupported: virtual interface
7 | virtual interface vi_t vi;
| ^~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_class_unsup_bad.v:8:1: Unsupported: virtual data type
8 | virtual vi_t vi2;
| ^~~~~~~
%Error: t/t_class_unsup_bad.v:29:24: Syntax error: 'const'/'rand'/'randc' not allowed before function/task declaration
29 | const function void func_const; endfunction
| ^~~~~~~~~~

View File

@ -0,0 +1,7 @@
va.addr=aa va.data=11 ia.addr=aa ia.data=11
vb.addr=bb vb.data=22 ib.addr=bb ib.data=22
ca.fa.addr=a0 ca.fa.data=11 ca.fa.addr=b0 ca.fb.data=22
cb.fa.addr=b0 cb.fa.data=22 cb.fa.addr=a0 cb.fb.data=11
gen.x[0].addr=a0 gen.x[1].addr=b0
gen='{x:'{top.t.ia, top.t.ib, null, null} }
*-* All Finished *-*

View File

@ -0,0 +1,22 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2020 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,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,72 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Arkadiusz Kozdra.
// SPDX-License-Identifier: CC0-1.0
// See also t_interface_virtual_bad.v
interface PBus;
logic req, grant;
logic [7:0] addr, data;
modport phy(input addr, ref data);
endinterface
typedef virtual PBus vpbus_t;
typedef vpbus_t vpbus2_t;
class Cls;
vpbus2_t fa, fb;
endclass
class Clsgen#(type T = logic);
T x[0:3];
endclass
module t (/*AUTOARG*/);
PBus ia, ib;
virtual PBus va, vb;
virtual PBus.phy pa, pb;
Cls ca, cb;
Clsgen#(virtual PBus) gen;
initial begin
va = ia;
vb = ib;
ca = new;
cb = new;
gen = new;
va.addr = 8'haa;
ia.data = 8'h11;
vb.addr = 8'hbb;
ib.data = 8'h22;
$display("va.addr=%x", va.addr, " va.data=%x", va.data, " ia.addr=%x", ia.addr, " ia.data=%x", ia.data);
$display("vb.addr=%x", vb.addr, " vb.data=%x", vb.data, " ib.addr=%x", ib.addr, " ib.data=%x", ib.data);
ca.fa = ia;
ca.fb = ib;
cb.fa = ib;
cb.fb = ia;
gen.x[0] = va;
gen.x[1] = vb;
pa = va;
pb = vb;
pb.addr = 8'hb0;
pa.addr = 8'ha0;
$display("ca.fa.addr=%x", ca.fa.addr, " ca.fa.data=%x", ca.fa.data, " ca.fa.addr=%x", ca.fb.addr, " ca.fb.data=%x", ca.fb.data);
$display("cb.fa.addr=%x", cb.fa.addr, " cb.fa.data=%x", cb.fa.data, " cb.fa.addr=%x", cb.fb.addr, " cb.fb.data=%x", cb.fb.data);
$display("gen.x[0].addr=%x", gen.x[0].addr, " gen.x[1].addr=%x", gen.x[1].addr);
$display("gen=%p", gen);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,34 @@
%Error: t/t_interface_virtual_bad.v:31:12: Operator ASSIGN expected 'PBus' interface on Assign RHS but 'q8__Viftop' is a different interface ('QBus').
: ... In instance t
31 | v8 = q8;
| ^~
%Error: t/t_interface_virtual_bad.v:35:12: Operator ASSIGN expected no interface modport on Assign RHS but got 'phy' modport.
: ... In instance t
35 | v8 = v8_phy;
| ^~~~~~
%Error: t/t_interface_virtual_bad.v:37:17: Operator ASSIGN expected non-interface on Assign RHS but 'p8__Viftop' is an interface.
: ... In instance t
37 | data = p8.phy;
| ^~~
%Error: t/t_interface_virtual_bad.v:38:14: Operator ASSIGN expected non-interface on Assign RHS but 'v8_phy' is an interface.
: ... In instance t
38 | data = v8_phy;
| ^~~~~~
%Error: t/t_interface_virtual_bad.v:39:14: Operator ASSIGN expected non-interface on Assign RHS but 'v8' is an interface.
: ... In instance t
39 | data = v8;
| ^~
%Error: t/t_interface_virtual_bad.v:40:14: Operator ASSIGN expected non-interface on Assign RHS but 'p8__Viftop' is an interface.
: ... In instance t
40 | data = p8;
| ^~
%Error: t/t_interface_virtual_bad.v:41:12: Operator ASSIGN expected 'PBus' interface on Assign RHS but 'data' is not an interface.
: ... In instance t
41 | v8 = data;
| ^~~~
%Error: t/t_interface_virtual_bad.v:44:79: Member 'gran' not found in interface 'PBus'
: ... In instance t
: ... Suggested alternative: 'grant'
44 | $display("q8.grant=", p8.grant, " v8.grant=", v8.grant, v8_phy.addr, v8.gran);
| ^~~~
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2020 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(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,49 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Arkadiusz Kozdra.
// SPDX-License-Identifier: CC0-1.0
// See also t_interface_virtual.v
interface PBus;
logic req, grant;
logic [7:0] addr, data;
modport phy(input addr, ref data);
endinterface
interface QBus;
endinterface
typedef virtual PBus vpbus_t;
module t (/*AUTOARG*/);
PBus p8;
QBus q8;
vpbus_t v8;
virtual PBus.phy v8_phy;
logic data;
initial begin
v8 = p8;
p8 = v8; // error
v8 = q8; // error
v8_phy = p8;
v8_phy = v8;
v8_phy = p8.phy;
v8 = v8_phy; // error
v8 = p8.phy; // error
data = p8.phy; // error
data = v8_phy; // error
data = v8; // error
data = p8; // error
v8 = data; // error
v8.grant = 1'b1;
$display("q8.grant=", p8.grant, " v8.grant=", v8.grant, v8_phy.addr, v8.gran);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,27 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003-2009 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);
top_filename("t/t_interface_virtual.v");
golden_filename("t/t_interface_virtual.out");
compile(
# Avoid inlining so we find bugs in the non-inliner connection code
verilator_flags2 => ["-fno-inline"],
);
execute(
check_finished => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,5 @@
%Error-UNSUPPORTED: t/t_interface_virtual_unused_bad.v:14:12: Unsupported: virtual interface never assigned any actual interface
14 | virtual QBus q8;
| ^~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2020 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(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,20 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Arkadiusz Kozdra.
// SPDX-License-Identifier: CC0-1.0
// See also t_interface_virtual.v
interface QBus;
endinterface
module t (/*AUTOARG*/);
virtual QBus q8;
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule