parent
2215d01d6b
commit
b14d65a787
|
|
@ -2649,13 +2649,167 @@ class LinkDotIfaceVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to extract a dotted path string from an AstDot tree
|
||||
// Returns empty string and sets hasPartSelect=true if part-select detected
|
||||
string extractDottedPath(AstNode* nodep, bool& hasPartSelect) {
|
||||
if (AstParseRef* const refp = VN_CAST(nodep, ParseRef)) {
|
||||
return refp->name();
|
||||
} else if (AstVarRef* const refp = VN_CAST(nodep, VarRef)) {
|
||||
return refp->name();
|
||||
} else if (AstDot* const dotp = VN_CAST(nodep, Dot)) {
|
||||
const string lhs = extractDottedPath(dotp->lhsp(), hasPartSelect);
|
||||
const string rhs = extractDottedPath(dotp->rhsp(), hasPartSelect);
|
||||
if (lhs.empty()) return rhs;
|
||||
if (rhs.empty()) return lhs;
|
||||
return lhs + "." + rhs;
|
||||
} else if (VN_IS(nodep, SelBit) || VN_IS(nodep, SelExtract)) {
|
||||
hasPartSelect = true;
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Helper to resolve remaining path through a nested interface
|
||||
// When findDotted() partially matches (okSymp set, baddot non-empty),
|
||||
// this follows the interface type to resolve the remaining path.
|
||||
// Returns the resolved symbol, or nullptr if not found.
|
||||
// On success, clears baddot; on partial match of multi-level path, updates baddot.
|
||||
VSymEnt* resolveNestedInterfacePath(FileLine* fl, VSymEnt* okSymp, string& baddot) {
|
||||
if (!okSymp || baddot.empty()) return nullptr;
|
||||
|
||||
static constexpr int MAX_NESTING_DEPTH = 64;
|
||||
VSymEnt* curOkSymp = okSymp;
|
||||
|
||||
for (int depth = 0; depth < MAX_NESTING_DEPTH; ++depth) {
|
||||
// Try to get interface from the partially-matched symbol
|
||||
AstIface* ifacep = nullptr;
|
||||
if (const AstCell* const cellp = VN_CAST(curOkSymp->nodep(), Cell)) {
|
||||
ifacep = VN_CAST(cellp->modp(), Iface);
|
||||
} else if (const AstVar* const varp = VN_CAST(curOkSymp->nodep(), Var)) {
|
||||
if (varp->isIfaceRef()) {
|
||||
if (const AstIfaceRefDType* const ifaceRefp
|
||||
= LinkDotState::ifaceRefFromArray(varp->dtypep())) {
|
||||
ifacep = ifaceRefp->ifaceViaCellp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ifacep || !m_statep->existsNodeSym(ifacep)) return nullptr;
|
||||
|
||||
VSymEnt* const ifaceSymp = m_statep->getNodeSym(ifacep);
|
||||
|
||||
if (baddot.find('.') == string::npos) {
|
||||
// Simple identifier - direct lookup
|
||||
VSymEnt* const symp = ifaceSymp->findIdFallback(baddot);
|
||||
if (symp) baddot.clear();
|
||||
return symp;
|
||||
}
|
||||
|
||||
// Multi-level path - use findDotted for partial resolution
|
||||
string remainingBaddot;
|
||||
VSymEnt* remainingOkSymp = nullptr;
|
||||
VSymEnt* const symp = m_statep->findDotted(fl, ifaceSymp, baddot, remainingBaddot,
|
||||
remainingOkSymp, true);
|
||||
if (symp) {
|
||||
baddot = remainingBaddot;
|
||||
return symp;
|
||||
}
|
||||
|
||||
// findDotted partially matched - check progress
|
||||
if (remainingBaddot == baddot || !remainingOkSymp) return nullptr;
|
||||
|
||||
// Continue resolving with updated state
|
||||
baddot = remainingBaddot;
|
||||
curOkSymp = remainingOkSymp;
|
||||
}
|
||||
|
||||
UINFO(1, "Nested interface resolution depth limit exceeded at " << fl << endl);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Resolve a modport expression to find the referenced symbol
|
||||
VSymEnt* resolveModportExpression(FileLine* fl, AstNodeExpr* exprp) {
|
||||
UINFO(5, " resolveModportExpression: " << exprp << endl);
|
||||
VSymEnt* symp = nullptr;
|
||||
if (AstParseRef* const refp = VN_CAST(exprp, ParseRef)) {
|
||||
// Simple variable reference: modport mp(input .a(sig_a))
|
||||
symp = m_curSymp->findIdFallback(refp->name());
|
||||
} else if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) {
|
||||
// Already resolved VarRef (can happen if iterateChildren resolved it)
|
||||
if (refp->varScopep()) {
|
||||
// During scope creation, VarRef may have VarScope already linked
|
||||
symp = m_statep->getNodeSym(refp->varScopep());
|
||||
} else if (refp->varp()) {
|
||||
symp = m_curSymp->findIdFallback(refp->varp()->name());
|
||||
}
|
||||
} else if (AstVarXRef* const refp = VN_CAST(exprp, VarXRef)) {
|
||||
// Resolved VarXRef (dotted reference resolved by earlier pass)
|
||||
if (refp->varp()) {
|
||||
// For nested interfaces, the var is in a different scope
|
||||
// First try to find the symbol via the var itself
|
||||
if (m_statep->existsNodeSym(refp->varp())) {
|
||||
symp = m_statep->getNodeSym(refp->varp());
|
||||
} else {
|
||||
// Fallback: look up in current scope
|
||||
symp = m_curSymp->findIdFallback(refp->varp()->name());
|
||||
}
|
||||
} else if (!refp->dotted().empty()) {
|
||||
// varp not set yet - use dotted path to find the symbol
|
||||
// The dotted part (e.g., "base") is the interface cell, and name is the member
|
||||
const string fullPath = refp->dotted() + "." + refp->name();
|
||||
string baddot;
|
||||
VSymEnt* okSymp = nullptr;
|
||||
symp = m_statep->findDotted(fl, m_curSymp, fullPath, baddot, okSymp, true);
|
||||
// Handle nested interface path when findDotted had partial match
|
||||
if (okSymp && !baddot.empty()) {
|
||||
VSymEnt* const resolved = resolveNestedInterfacePath(fl, okSymp, baddot);
|
||||
if (resolved) symp = resolved;
|
||||
}
|
||||
}
|
||||
} else if (AstDot* const dotp = VN_CAST(exprp, Dot)) {
|
||||
// Dotted path: modport mp(input .a(inner.sig))
|
||||
bool hasPartSelect = false;
|
||||
const string dottedPath = extractDottedPath(dotp, hasPartSelect);
|
||||
if (hasPartSelect) {
|
||||
fl->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4)");
|
||||
} else {
|
||||
string baddot;
|
||||
VSymEnt* okSymp = nullptr;
|
||||
symp = m_statep->findDotted(fl, m_curSymp, dottedPath, baddot, okSymp, true);
|
||||
|
||||
// Handle nested interface path when findDotted had partial match
|
||||
if (okSymp && !baddot.empty()) {
|
||||
VSymEnt* const resolved = resolveNestedInterfacePath(fl, okSymp, baddot);
|
||||
if (resolved) symp = resolved;
|
||||
}
|
||||
|
||||
if (!symp || !baddot.empty()) {
|
||||
fl->v3error("Can't find modport expression target: "
|
||||
<< AstNode::prettyNameQ(dottedPath));
|
||||
symp = nullptr;
|
||||
}
|
||||
}
|
||||
} else if (VN_IS(exprp, SelBit) || VN_IS(exprp, SelExtract)) {
|
||||
// Part select expressions not yet supported
|
||||
fl->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4)");
|
||||
} else {
|
||||
// Other expression types not supported
|
||||
fl->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Complex modport expression (IEEE 1800-2023 25.5.4)");
|
||||
}
|
||||
return symp;
|
||||
}
|
||||
|
||||
void visit(AstModportVarRef* nodep) override { // IfaceVisitor::
|
||||
UINFO(5, " fiv: " << nodep);
|
||||
iterateChildren(nodep);
|
||||
VSymEnt* symp = nullptr;
|
||||
if (nodep->exprp()) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)");
|
||||
// Modport expression syntax: modport mp(input .port_name(expression))
|
||||
symp = resolveModportExpression(nodep->fileline(), nodep->exprp());
|
||||
} else {
|
||||
symp = m_curSymp->findIdFallback(nodep->name());
|
||||
}
|
||||
|
|
@ -2665,11 +2819,30 @@ class LinkDotIfaceVisitor final : public VNVisitor {
|
|||
// Make symbol under modport that points at the _interface_'s var via the modport.
|
||||
// (Need modport still to test input/output markings)
|
||||
nodep->varp(varp);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, nullptr /*package*/);
|
||||
if (nodep->exprp()) {
|
||||
// For modport expressions, insert symbol pointing to the underlying var (not the
|
||||
// ModportVarRef which will be deleted during scope creation). The virtual port
|
||||
// name maps to the real signal's var.
|
||||
VSymEnt* const subSymp
|
||||
= m_statep->insertSym(m_curSymp, nodep->name(), varp, nullptr /*package*/);
|
||||
m_statep->insertScopeAlias(LinkDotState::SAMN_MODPORT, subSymp, symp);
|
||||
} else {
|
||||
// For regular modport items, insert symbol pointing to ModportVarRef for
|
||||
// input/output marking tests.
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, nullptr /*package*/);
|
||||
}
|
||||
} else if (AstVarScope* const vscp = VN_CAST(symp->nodep(), VarScope)) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
nodep->varp(vscp->varp());
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), vscp, nullptr /*package*/);
|
||||
// Only insert symbol for modport expression virtual ports (exprp set).
|
||||
// For regular modport items, don't insert into the shared modport table because
|
||||
// each instance would overwrite the previous one, causing wrong VarScope lookups.
|
||||
// Regular items are found via the modport's fallback to the interface.
|
||||
if (nodep->exprp()) {
|
||||
VSymEnt* const subSymp
|
||||
= m_statep->insertSym(m_curSymp, nodep->name(), vscp, nullptr /*package*/);
|
||||
m_statep->insertScopeAlias(LinkDotState::SAMN_MODPORT, subSymp, symp);
|
||||
}
|
||||
} else {
|
||||
nodep->v3error("Modport item is not a variable: " << nodep->prettyNameQ());
|
||||
}
|
||||
|
|
@ -2866,6 +3039,46 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Look up a virtual port through modport expression mapping.
|
||||
// When a dotted reference uses a modport with expression syntax, the virtual port name
|
||||
// maps to the real signal. This helper resolves that mapping during scope creation.
|
||||
// Returns the VarScope for the real signal, or nullptr if not found.
|
||||
AstVarScope* findVirtualPortVarScope(VSymEnt* dotSymp, const string& portName) {
|
||||
const AstVarScope* const dotVscp = VN_CAST(dotSymp->nodep(), VarScope);
|
||||
const AstVar* const dotVarp = dotVscp ? dotVscp->varp() : nullptr;
|
||||
if (!dotVarp) return nullptr;
|
||||
|
||||
AstNodeDType* dtypep = dotVarp->childDTypep();
|
||||
if (!dtypep) dtypep = dotVarp->subDTypep();
|
||||
if (!dtypep) return nullptr;
|
||||
const AstIfaceRefDType* const ifaceRefp = VN_CAST(dtypep, IfaceRefDType);
|
||||
if (!ifaceRefp || !ifaceRefp->modportp() || !ifaceRefp->ifaceViaCellp()) return nullptr;
|
||||
|
||||
// Get the interface cell's symbol table
|
||||
VSymEnt* const ifaceCellSymp = m_statep->getNodeSym(ifaceRefp->ifaceViaCellp());
|
||||
|
||||
// Look up modport by name in interface cell
|
||||
VSymEnt* const modportSymp = ifaceCellSymp->findIdFallback(ifaceRefp->modportp()->name());
|
||||
if (!modportSymp) return nullptr;
|
||||
|
||||
// Look up virtual port name in modport symbol table
|
||||
string baddot;
|
||||
VSymEnt* const virtPortp = m_statep->findSymPrefixed(modportSymp, portName, baddot, true);
|
||||
if (!virtPortp) return nullptr;
|
||||
|
||||
// Symbol may point to VarScope or Var
|
||||
if (AstVarScope* vscp = VN_CAST(virtPortp->nodep(), VarScope)) return vscp;
|
||||
|
||||
// Symbol points to Var - look up VarScope by name in interface cell
|
||||
if (const AstVar* const realVarp = VN_CAST(virtPortp->nodep(), Var)) {
|
||||
VSymEnt* const realFoundp
|
||||
= m_statep->findSymPrefixed(ifaceCellSymp, realVarp->name(), baddot, true);
|
||||
return realFoundp ? VN_CAST(realFoundp->nodep(), VarScope) : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AstNodeStmt* addImplicitSuperNewCall(AstFunc* const nodep,
|
||||
const AstClassExtends* const classExtendsp) {
|
||||
// Returns the added node
|
||||
|
|
@ -3863,6 +4076,24 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
} else {
|
||||
foundp = m_ds.m_dotSymp->findIdFlat(nodep->name());
|
||||
}
|
||||
// If not found in modport, check interface fallback for parameters.
|
||||
// Parameters are always visible through a modport (IEEE 1800-2023 25.5).
|
||||
// This mirrors the VarXRef modport parameter fallback in visit(AstVarXRef).
|
||||
if (!foundp && VN_IS(m_ds.m_dotSymp->nodep(), Modport)
|
||||
&& m_ds.m_dotSymp->fallbackp()) {
|
||||
VSymEnt* const ifaceFoundp
|
||||
= m_ds.m_dotSymp->fallbackp()->findIdFlat(nodep->name());
|
||||
if (ifaceFoundp) {
|
||||
if (const AstVar* const varp = VN_CAST(ifaceFoundp->nodep(), Var)) {
|
||||
if (varp->isParam()) foundp = ifaceFoundp;
|
||||
}
|
||||
}
|
||||
}
|
||||
// When flat lookup in modport fails, provide dotSymp for error diagnostics
|
||||
// so the "Known scopes under..." hint appears (restores pre-routing behavior)
|
||||
if (!foundp && !okSymp && VN_IS(m_ds.m_dotSymp->nodep(), Modport)) {
|
||||
okSymp = m_ds.m_dotSymp;
|
||||
}
|
||||
if (foundp) {
|
||||
UINFO(9, indent() << "found=se" << cvtToHex(foundp) << " exp=" << expectWhat
|
||||
<< " n=" << foundp->nodep());
|
||||
|
|
@ -3959,7 +4190,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
// Really this is a scope reference into an interface
|
||||
UINFO(9, indent() << "varref-ifaceref " << m_ds.m_dotText << " " << nodep);
|
||||
m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name());
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->ifaceViaCellp());
|
||||
// If modport specified, use modport symbol table for lookup of virtual ports
|
||||
if (ifacerefp->modportp() && m_statep->existsNodeSym(ifacerefp->modportp())) {
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->modportp());
|
||||
} else {
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->ifaceViaCellp());
|
||||
}
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
ok = true;
|
||||
AstNode* const newp = new AstVarRef{nodep->fileline(), varp, VAccess::READ};
|
||||
|
|
@ -4109,6 +4345,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
m_ds.m_dotSymp = foundp;
|
||||
if (m_ds.m_dotText != "") m_ds.m_dotText += "." + nodep->name();
|
||||
ok = m_ds.m_dotPos == DP_SCOPE || m_ds.m_dotPos == DP_FIRST;
|
||||
} else if (const AstModportClockingRef* const clockingRefp
|
||||
= VN_CAST(foundp->nodep(), ModportClockingRef)) {
|
||||
// Clocking block accessed through a modport - redirect to actual clocking
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(clockingRefp->clockingp());
|
||||
if (m_ds.m_dotText != "") m_ds.m_dotText += "." + nodep->name();
|
||||
ok = m_ds.m_dotPos == DP_SCOPE || m_ds.m_dotPos == DP_FIRST;
|
||||
} else if (const AstNodeFTask* const ftaskp = VN_CAST(foundp->nodep(), NodeFTask)) {
|
||||
if (!ftaskp->isFunction() || ftaskp->classMethod()) {
|
||||
ok = m_ds.m_dotPos == DP_NONE;
|
||||
|
|
@ -4396,7 +4638,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
bool modport = false;
|
||||
if (const AstVar* varp = VN_CAST(dotSymp->nodep(), Var)) {
|
||||
if (const AstVar* const varp = VN_CAST(dotSymp->nodep(), Var)) {
|
||||
if (const AstIfaceRefDType* const ifaceRefp
|
||||
= VN_CAST(varp->childDTypep(), IfaceRefDType)) {
|
||||
if (ifaceRefp->modportp()) {
|
||||
|
|
@ -4474,7 +4716,9 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
} else {
|
||||
VSymEnt* const foundp
|
||||
= m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, true);
|
||||
AstVarScope* vscp = foundp ? VN_AS(foundp->nodep(), VarScope) : nullptr;
|
||||
AstVarScope* vscp = foundp ? VN_CAST(foundp->nodep(), VarScope) : nullptr;
|
||||
// Handle modport expression virtual ports
|
||||
if (!vscp) vscp = findVirtualPortVarScope(dotSymp, nodep->name());
|
||||
// If found, check if it's ok to access in case it's in a hier_block
|
||||
if (vscp && errorHierNonPort(nodep, vscp->varp(), dotSymp)) return;
|
||||
if (!vscp) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
%Error: t/t_interface_generic_modport_bad3.v:18:11: Can't find definition of 'v' in dotted signal: 'a.v'
|
||||
%Error: t/t_interface_generic_modport_bad3.v:18:11: Can't find definition of 'v' in dotted variable/method: 'a.v'
|
||||
: ... note: In instance 't.genericModule'
|
||||
18 | if (a.v != 7) $stop;
|
||||
| ^
|
||||
... Known scopes under 'v': <no instances found>
|
||||
... Known scopes under 'a': <no instances found>
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
%Error: t/t_interface_mismodport_bad.v:32:12: Can't find definition of 'bad' in dotted signal: 'isub.bad'
|
||||
%Error: t/t_interface_mismodport_bad.v:32:12: Can't find definition of 'bad' in dotted variable/method: 'isub.bad'
|
||||
32 | isub.bad = i_value;
|
||||
| ^~~
|
||||
... Known scopes under 'bad': <no instances found>
|
||||
... Known scopes under 'isub': <no instances found>
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
%Error-UNSUPPORTED: t/t_interface_modport_expr.v:15:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)
|
||||
15 | modport mp1(input .a(sig_a), output .b(sig_b));
|
||||
| ^
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: t/t_interface_modport_expr.v:15:22: Modport item not found: 'a'
|
||||
15 | modport mp1(input .a(sig_a), output .b(sig_b));
|
||||
| ^
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr.v:15:40: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)
|
||||
15 | modport mp1(input .a(sig_a), output .b(sig_b));
|
||||
| ^
|
||||
%Error: t/t_interface_modport_expr.v:15:40: Modport item not found: 'b'
|
||||
15 | modport mp1(input .a(sig_a), output .b(sig_b));
|
||||
| ^
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr.v:16:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)
|
||||
16 | modport mp2(input .a(sig_c), output .b(sig_d));
|
||||
| ^
|
||||
%Error: t/t_interface_modport_expr.v:16:22: Modport item not found: 'a'
|
||||
16 | modport mp2(input .a(sig_c), output .b(sig_d));
|
||||
| ^
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr.v:16:40: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)
|
||||
16 | modport mp2(input .a(sig_c), output .b(sig_d));
|
||||
| ^
|
||||
%Error: t/t_interface_modport_expr.v:16:40: Modport item not found: 'b'
|
||||
16 | modport mp2(input .a(sig_c), output .b(sig_d));
|
||||
| ^
|
||||
%Error: t/t_interface_modport_expr.v:28:18: Can't find definition of 'a' in dotted variable/method: 'i.a'
|
||||
28 | assign i.b = i.a;
|
||||
| ^
|
||||
%Error: t/t_interface_modport_expr.v:28:12: Can't find definition of 'b' in dotted variable/method: 'i.b'
|
||||
28 | assign i.b = i.a;
|
||||
| ^
|
||||
%Error: t/t_interface_modport_expr.v:22:18: Can't find definition of 'a' in dotted variable/method: 'i.a'
|
||||
22 | assign i.b = i.a;
|
||||
| ^
|
||||
%Error: t/t_interface_modport_expr.v:22:12: Can't find definition of 'b' in dotted variable/method: 'i.b'
|
||||
22 | assign i.b = i.a;
|
||||
| ^
|
||||
%Error: Exiting due to
|
||||
|
|
@ -4,18 +4,15 @@
|
|||
# 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: 2025 Wilson Snyder
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(fails=test.vlt_all,
|
||||
expect_filename=test.golden_filename,
|
||||
verilator_flags2=["--binary"])
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
if not test.vlt_all:
|
||||
test.execute()
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -9,11 +9,18 @@
|
|||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
interface my_if;
|
||||
logic sig_a, sig_b, sig_c, sig_d;
|
||||
interface my_if #(parameter WIDTH = 1);
|
||||
logic [WIDTH-1:0] sig_a, sig_b, sig_c, sig_d;
|
||||
logic [WIDTH-1:0] sig_e, sig_f;
|
||||
// Multiple expressions same direction
|
||||
logic [WIDTH-1:0] m1, m2, m3;
|
||||
|
||||
modport mp1(input .a(sig_a), output .b(sig_b));
|
||||
modport mp2(input .a(sig_c), output .b(sig_d));
|
||||
// Mixed regular and expression items
|
||||
modport mp3(input sig_e, output .f(sig_f));
|
||||
// Multiple expressions with same direction
|
||||
modport mp4(input .in1(m1), input .in2(m2), output .out(m3));
|
||||
endinterface
|
||||
|
||||
module mod1 (
|
||||
|
|
@ -28,17 +35,35 @@ module mod2 (
|
|||
assign i.b = i.a;
|
||||
endmodule
|
||||
|
||||
module top ();
|
||||
my_if myIf ();
|
||||
assign myIf.sig_a = 1'b1, myIf.sig_c = 1'b1;
|
||||
module mod3 (
|
||||
my_if.mp3 i
|
||||
);
|
||||
assign i.f = i.sig_e; // sig_e is regular, f is expression
|
||||
endmodule
|
||||
|
||||
mod1 mod1Instance (myIf);
|
||||
mod2 mod2Instance (myIf);
|
||||
module mod4 (
|
||||
my_if.mp4 i
|
||||
);
|
||||
assign i.out = i.in1 ^ i.in2;
|
||||
endmodule
|
||||
|
||||
module top ();
|
||||
my_if #(.WIDTH(8)) myIf ();
|
||||
assign myIf.sig_a = 8'h42, myIf.sig_c = 8'hAB;
|
||||
assign myIf.sig_e = 8'hCD;
|
||||
assign myIf.m1 = 8'hF0, myIf.m2 = 8'h0F;
|
||||
|
||||
mod1 mod1i (myIf.mp1);
|
||||
mod2 mod2i (myIf.mp2);
|
||||
mod3 mod3i (myIf.mp3);
|
||||
mod4 mod4i (myIf.mp4);
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
`checkh(myIf.sig_a, myIf.sig_b);
|
||||
`checkh(myIf.sig_c, myIf.sig_d);
|
||||
`checkh(myIf.sig_b, 8'h42); // mp1: b = a
|
||||
`checkh(myIf.sig_d, 8'hAB); // mp2: b = a
|
||||
`checkh(myIf.sig_f, 8'hCD); // mp3: f = sig_e
|
||||
`checkh(myIf.m3, 8'hFF); // mp4: out = in1 ^ in2
|
||||
#1;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
%Error-UNSUPPORTED: t/t_interface_modport_expr_array.v:26:13: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4)
|
||||
26 | output .ch0_wr(ch[0].wr), input .ch0_rd(ch[0].rd),
|
||||
| ^~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: t/t_interface_modport_expr_array.v:26:13: Modport item not found: 'ch0_wr'
|
||||
26 | output .ch0_wr(ch[0].wr), input .ch0_rd(ch[0].rd),
|
||||
| ^~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr_array.v:26:38: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4)
|
||||
26 | output .ch0_wr(ch[0].wr), input .ch0_rd(ch[0].rd),
|
||||
| ^~~~~~
|
||||
%Error: t/t_interface_modport_expr_array.v:26:38: Modport item not found: 'ch0_rd'
|
||||
26 | output .ch0_wr(ch[0].wr), input .ch0_rd(ch[0].rd),
|
||||
| ^~~~~~
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr_array.v:27:13: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4)
|
||||
27 | output .ch1_wr(ch[1].wr), input .ch1_rd(ch[1].rd)
|
||||
| ^~~~~~
|
||||
%Error: t/t_interface_modport_expr_array.v:27:13: Modport item not found: 'ch1_wr'
|
||||
27 | output .ch1_wr(ch[1].wr), input .ch1_rd(ch[1].rd)
|
||||
| ^~~~~~
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr_array.v:27:38: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4)
|
||||
27 | output .ch1_wr(ch[1].wr), input .ch1_rd(ch[1].rd)
|
||||
| ^~~~~~
|
||||
%Error: t/t_interface_modport_expr_array.v:27:38: Modport item not found: 'ch1_rd'
|
||||
27 | output .ch1_wr(ch[1].wr), input .ch1_rd(ch[1].rd)
|
||||
| ^~~~~~
|
||||
%Error: t/t_interface_modport_expr_array.v:33:29: Can't find definition of 'ch0_wr' in dotted variable/method: 'port.ch0_wr'
|
||||
33 | assign port.ch0_rd = port.ch0_wr + 8'h10;
|
||||
| ^~~~~~
|
||||
... Known scopes under 'port': <no instances found>
|
||||
%Error: t/t_interface_modport_expr_array.v:33:15: Can't find definition of 'ch0_rd' in dotted variable/method: 'port.ch0_rd'
|
||||
33 | assign port.ch0_rd = port.ch0_wr + 8'h10;
|
||||
| ^~~~~~
|
||||
... Known scopes under 'port': <no instances found>
|
||||
%Error: t/t_interface_modport_expr_array.v:34:29: Can't find definition of 'ch1_wr' in dotted variable/method: 'port.ch1_wr'
|
||||
34 | assign port.ch1_rd = port.ch1_wr + 8'h20;
|
||||
| ^~~~~~
|
||||
... Known scopes under 'port': <no instances found>
|
||||
%Error: t/t_interface_modport_expr_array.v:34:15: Can't find definition of 'ch1_rd' in dotted variable/method: 'port.ch1_rd'
|
||||
34 | assign port.ch1_rd = port.ch1_wr + 8'h20;
|
||||
| ^~~~~~
|
||||
... Known scopes under 'port': <no instances found>
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/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(fails=test.vlt_all,
|
||||
expect_filename=test.golden_filename,
|
||||
verilator_flags2=["--binary"])
|
||||
|
||||
if not test.vlt_all:
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Leela Pakanati
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test modport expressions with arrayed interface instances (unsupported)
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
interface base_if;
|
||||
logic [7:0] wr;
|
||||
logic [7:0] rd;
|
||||
modport host(output wr, input rd);
|
||||
modport dev(input wr, output rd);
|
||||
endinterface
|
||||
|
||||
interface container_if;
|
||||
base_if ch[2]();
|
||||
|
||||
// Modport expressions accessing arrayed interface instances
|
||||
modport mp(
|
||||
output .ch0_wr(ch[0].wr), input .ch0_rd(ch[0].rd),
|
||||
output .ch1_wr(ch[1].wr), input .ch1_rd(ch[1].rd)
|
||||
);
|
||||
endinterface
|
||||
|
||||
module consumer(container_if.mp port);
|
||||
// Access through modport expression virtual ports
|
||||
assign port.ch0_rd = port.ch0_wr + 8'h10;
|
||||
assign port.ch1_rd = port.ch1_wr + 8'h20;
|
||||
endmodule
|
||||
|
||||
module top;
|
||||
container_if cont();
|
||||
|
||||
consumer m_cons(.port(cont));
|
||||
|
||||
initial begin
|
||||
cont.ch[0].wr = 8'hA0;
|
||||
cont.ch[1].wr = 8'hB0;
|
||||
#1;
|
||||
`checkh(cont.ch[0].rd, 8'hB0);
|
||||
`checkh(cont.ch[1].rd, 8'hD0);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
%Error: t/t_interface_modport_expr_bad.v:10:22: Can't find modport expression target: 'nonexist.sig'
|
||||
10 | modport mp1(input .in(nonexist.sig));
|
||||
| ^~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_interface_modport_expr_bad.v:10:22: Modport item not found: 'in'
|
||||
10 | modport mp1(input .in(nonexist.sig));
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr_bad.v:12:22: Unsupported: Complex modport expression (IEEE 1800-2023 25.5.4)
|
||||
12 | modport mp2(input .in(~a));
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: t/t_interface_modport_expr_bad.v:12:22: Modport item not found: 'in'
|
||||
12 | modport mp2(input .in(~a));
|
||||
| ^~
|
||||
%Error: t/t_interface_modport_expr_bad.v:10:25: Can't find definition of scope/variable: 'nonexist'
|
||||
10 | modport mp1(input .in(nonexist.sig));
|
||||
| ^~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/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(fails=test.vlt_all,
|
||||
expect_filename=test.golden_filename,
|
||||
verilator_flags2=["--binary"])
|
||||
|
||||
if not test.vlt_all:
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Leela Pakanati
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface iface;
|
||||
logic a, b;
|
||||
// Dotted path to non-existent signal
|
||||
modport mp1(input .in(nonexist.sig));
|
||||
// Complex expression not supported as modport expression
|
||||
modport mp2(input .in(~a));
|
||||
endinterface
|
||||
|
||||
module t;
|
||||
iface intf ();
|
||||
endmodule
|
||||
|
|
@ -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,142 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Leela Pakanati
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test modport expressions with hierarchical module instantiation
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
// ============================================================
|
||||
// Scenario A: Single-level hierarchy + expression modport
|
||||
// ============================================================
|
||||
|
||||
interface if_a;
|
||||
logic [7:0] raw_in;
|
||||
logic [7:0] raw_out;
|
||||
modport mp(input .data_in(raw_in), output .data_out(raw_out));
|
||||
endinterface
|
||||
|
||||
// Leaf module: uses modport expression virtual ports
|
||||
module a_leaf(if_a.mp port);
|
||||
assign port.data_out = port.data_in + 8'h10;
|
||||
endmodule
|
||||
|
||||
// Mid-level module: passes interface down
|
||||
module a_mid(if_a.mp port);
|
||||
a_leaf u_leaf(.port(port));
|
||||
endmodule
|
||||
|
||||
// ============================================================
|
||||
// Scenario B: 2-level deep hierarchy
|
||||
// ============================================================
|
||||
|
||||
interface if_b;
|
||||
logic [15:0] x;
|
||||
logic [15:0] y;
|
||||
modport mp(input .alpha(x), output .beta(y));
|
||||
endinterface
|
||||
|
||||
module b_leaf(if_b.mp port);
|
||||
assign port.beta = port.alpha ^ 16'hFFFF;
|
||||
endmodule
|
||||
|
||||
module b_mid(if_b.mp port);
|
||||
b_leaf u_leaf(.port(port));
|
||||
endmodule
|
||||
|
||||
module b_top_wrap(if_b.mp port);
|
||||
b_mid u_mid(.port(port));
|
||||
endmodule
|
||||
|
||||
// ============================================================
|
||||
// Scenario C: Nested interface + expression modport + hierarchy
|
||||
// ============================================================
|
||||
|
||||
interface if_c_inner;
|
||||
logic [7:0] val;
|
||||
endinterface
|
||||
|
||||
interface if_c_outer;
|
||||
if_c_inner inner();
|
||||
modport mp(output .w(inner.val), input .r(inner.val));
|
||||
endinterface
|
||||
|
||||
module c_leaf(if_c_outer.mp port);
|
||||
assign port.w = 8'hBE;
|
||||
wire [7:0] c_read = port.r;
|
||||
endmodule
|
||||
|
||||
module c_mid(if_c_outer.mp port);
|
||||
c_leaf u_leaf(.port(port));
|
||||
endmodule
|
||||
|
||||
// ============================================================
|
||||
// Scenario D: Multiple instances of same wrapper (no cross-talk)
|
||||
// ============================================================
|
||||
|
||||
interface if_d;
|
||||
logic [7:0] din;
|
||||
logic [7:0] dout;
|
||||
modport mp(input .vi(din), output .vo(dout));
|
||||
endinterface
|
||||
|
||||
module d_leaf(if_d.mp port);
|
||||
assign port.vo = port.vi + 8'h01;
|
||||
endmodule
|
||||
|
||||
module d_mid(if_d.mp port);
|
||||
d_leaf u_leaf(.port(port));
|
||||
endmodule
|
||||
|
||||
// ============================================================
|
||||
// Top module
|
||||
// ============================================================
|
||||
|
||||
module top;
|
||||
|
||||
// --- Scenario A ---
|
||||
if_a ifa();
|
||||
a_mid u_a(.port(ifa));
|
||||
|
||||
// --- Scenario B ---
|
||||
if_b ifb();
|
||||
b_top_wrap u_b(.port(ifb));
|
||||
|
||||
// --- Scenario C ---
|
||||
if_c_outer ifc();
|
||||
c_mid u_c(.port(ifc));
|
||||
|
||||
// --- Scenario D ---
|
||||
if_d ifd();
|
||||
d_mid u_d(.port(ifd));
|
||||
|
||||
initial begin
|
||||
// Scenario A: single-level hierarchy
|
||||
ifa.raw_in = 8'h20;
|
||||
#1;
|
||||
`checkh(ifa.raw_out, 8'h30);
|
||||
|
||||
// Scenario B: 2-level deep hierarchy
|
||||
ifb.x = 16'h1234;
|
||||
#1;
|
||||
`checkh(ifb.y, 16'hEDCB);
|
||||
|
||||
// Scenario C: nested interface + hierarchy
|
||||
#1;
|
||||
`checkh(ifc.inner.val, 8'hBE);
|
||||
`checkh(u_c.u_leaf.c_read, 8'hBE);
|
||||
|
||||
// Scenario D: single instance through hierarchy
|
||||
ifd.din = 8'h10;
|
||||
#1;
|
||||
`checkh(ifd.dout, 8'h11);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/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.top_filename = "t/t_interface_modport_expr_hier.v"
|
||||
|
||||
test.compile(verilator_flags2=["--binary", "-fno-inline"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -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,130 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Leela Pakanati
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test modport expressions with nested interfaces
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
interface base_reg_if;
|
||||
logic [7:0] wr;
|
||||
logic [7:0] rd;
|
||||
modport host(output wr, input rd);
|
||||
modport dev(input wr, output rd);
|
||||
endinterface
|
||||
|
||||
interface example_reg_if;
|
||||
logic [15:0] wr;
|
||||
logic [15:0] rd;
|
||||
modport host(output wr, input rd);
|
||||
modport dev(input wr, output rd);
|
||||
endinterface
|
||||
|
||||
interface app_reg_if;
|
||||
base_reg_if base();
|
||||
example_reg_if example();
|
||||
|
||||
// Use modport expressions to expose nested interface signals
|
||||
modport host(
|
||||
output .base_wr(base.wr), input .base_rd(base.rd),
|
||||
output .example_wr(example.wr), input .example_rd(example.rd)
|
||||
);
|
||||
modport dev(
|
||||
input .base_wr(base.wr), output .base_rd(base.rd),
|
||||
input .example_wr(example.wr), output .example_rd(example.rd)
|
||||
);
|
||||
endinterface
|
||||
|
||||
// Deep nesting test (3 levels)
|
||||
interface inner_if;
|
||||
logic [7:0] data;
|
||||
modport producer(output data);
|
||||
modport consumer(input data);
|
||||
endinterface
|
||||
|
||||
interface middle_if;
|
||||
inner_if inner();
|
||||
endinterface
|
||||
|
||||
interface outer_if;
|
||||
middle_if middle();
|
||||
|
||||
// 3-level deep modport expression
|
||||
modport mp(
|
||||
output .deep_out(middle.inner.data),
|
||||
input .deep_in(middle.inner.data)
|
||||
);
|
||||
endinterface
|
||||
|
||||
module deep_consumer(outer_if.mp port);
|
||||
assign port.deep_out = 8'hDE;
|
||||
// Verify reading through deep_in virtual port
|
||||
wire [7:0] deep_in_val = port.deep_in;
|
||||
endmodule
|
||||
|
||||
// 4-level deep nesting test
|
||||
interface level1_if;
|
||||
logic [7:0] val;
|
||||
endinterface
|
||||
|
||||
interface level2_if;
|
||||
level1_if l1();
|
||||
endinterface
|
||||
|
||||
interface level3_if;
|
||||
level2_if l2();
|
||||
endinterface
|
||||
|
||||
interface level4_if;
|
||||
level3_if l3();
|
||||
|
||||
// 4-level deep modport expression
|
||||
modport mp(
|
||||
output .deep4_w(l3.l2.l1.val),
|
||||
input .deep4_r(l3.l2.l1.val)
|
||||
);
|
||||
endinterface
|
||||
|
||||
module deep4_consumer(level4_if.mp port);
|
||||
assign port.deep4_w = 8'h4D;
|
||||
wire [7:0] deep4_read = port.deep4_r;
|
||||
endmodule
|
||||
|
||||
module app_consumer (
|
||||
app_reg_if.dev i_app_regs
|
||||
);
|
||||
// Access through modport expression virtual ports
|
||||
assign i_app_regs.base_rd = i_app_regs.base_wr + 8'h1;
|
||||
assign i_app_regs.example_rd = i_app_regs.example_wr + 16'h1;
|
||||
endmodule
|
||||
|
||||
module top;
|
||||
app_reg_if app_regs();
|
||||
outer_if outer();
|
||||
level4_if lev4();
|
||||
|
||||
app_consumer m_app(.i_app_regs(app_regs));
|
||||
deep_consumer m_deep(.port(outer));
|
||||
deep4_consumer m_deep4(.port(lev4));
|
||||
|
||||
initial begin
|
||||
app_regs.base.wr = 8'hAB;
|
||||
app_regs.example.wr = 16'hCDEF;
|
||||
#1;
|
||||
`checkh(app_regs.base.rd, 8'hAC);
|
||||
`checkh(app_regs.example.rd, 16'hCDF0);
|
||||
// Verify 3-level deep nesting
|
||||
`checkh(outer.middle.inner.data, 8'hDE);
|
||||
`checkh(m_deep.deep_in_val, 8'hDE);
|
||||
// Verify 4-level deep nesting
|
||||
`checkh(lev4.l3.l2.l1.val, 8'h4D);
|
||||
`checkh(m_deep4.deep4_read, 8'h4D);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/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.top_filename = "t/t_interface_modport_expr_nested.v"
|
||||
|
||||
test.compile(verilator_flags2=["--binary", "-fno-inline"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#!/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.top_filename = "t/t_interface_modport_expr.v"
|
||||
|
||||
test.compile(verilator_flags2=["--binary", "-fno-inline"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:16:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:16:22: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4)
|
||||
16 | modport mp1(input .in(a[7:0]), output .out(b));
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
|
|
@ -6,34 +6,18 @@
|
|||
16 | modport mp1(input .in(a[7:0]), output .out(b));
|
||||
| ^~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:16:42: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)
|
||||
16 | modport mp1(input .in(a[7:0]), output .out(b));
|
||||
| ^~~
|
||||
%Error: t/t_interface_modport_expr_partsel.v:16:42: Modport item not found: 'out'
|
||||
16 | modport mp1(input .in(a[7:0]), output .out(b));
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:17:22: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:17:22: Unsupported: Modport expression with part select (IEEE 1800-2023 25.5.4)
|
||||
17 | modport mp2(input .in(a[15:8]), output .out(c));
|
||||
| ^~
|
||||
%Error: t/t_interface_modport_expr_partsel.v:17:22: Modport item not found: 'in'
|
||||
17 | modport mp2(input .in(a[15:8]), output .out(c));
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_interface_modport_expr_partsel.v:17:43: Unsupported: Modport expressions (IEEE 1800-2023 25.5.4)
|
||||
17 | modport mp2(input .in(a[15:8]), output .out(c));
|
||||
| ^~~
|
||||
%Error: t/t_interface_modport_expr_partsel.v:17:43: Modport item not found: 'out'
|
||||
17 | modport mp2(input .in(a[15:8]), output .out(c));
|
||||
| ^~~
|
||||
%Error: t/t_interface_modport_expr_partsel.v:29:21: Can't find definition of 'in' in dotted variable/method: 'i.in'
|
||||
29 | assign i.out = ~i.in;
|
||||
| ^~
|
||||
%Error: t/t_interface_modport_expr_partsel.v:29:12: Can't find definition of 'out' in dotted variable/method: 'i.out'
|
||||
29 | assign i.out = ~i.in;
|
||||
| ^~~
|
||||
... Known scopes under 'i': <no instances found>
|
||||
%Error: t/t_interface_modport_expr_partsel.v:23:20: Can't find definition of 'in' in dotted variable/method: 'i.in'
|
||||
23 | assign i.out = i.in;
|
||||
| ^~
|
||||
%Error: t/t_interface_modport_expr_partsel.v:23:12: Can't find definition of 'out' in dotted variable/method: 'i.out'
|
||||
23 | assign i.out = i.in;
|
||||
| ^~~
|
||||
... Known scopes under 'i': <no instances found>
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
# 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: 2025 Wilson Snyder
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
%Error: t/t_mod_interface_clocking_bad.v:16:41: Modport item not found: 'cx'
|
||||
16 | modport mp(input clk, clocking reset, clocking cx);
|
||||
| ^~~~~~~~
|
||||
%Error: t/t_mod_interface_clocking_bad.v:25:10: Can't find definition of 'cb'
|
||||
%Error: t/t_mod_interface_clocking_bad.v:25:7: Can't find definition of 'cb' in dotted scope/variable: 'x.cb'
|
||||
25 | x.cb.reset <= 1;
|
||||
| ^~~~~
|
||||
| ^~
|
||||
... Known scopes under 'x': <no instances found>
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
Loading…
Reference in New Issue