Support interfaces and modports, bug102.

This commit is contained in:
Wilson Snyder 2013-05-27 21:39:19 -04:00
parent 7c834ad118
commit 23bb045a72
56 changed files with 1914 additions and 131 deletions

View File

@ -5,6 +5,8 @@ indicates the contributor was also the author of the fix; Thanks!
* Verilator 3.848 devel
** Support interfaces and modports, bug102. [Byron Bradley, Jeremy Bennett]
**** Fix arrayed input compile error, bug645. [Krzysztof Jankowski]
**** Fix GCC version runtime changes, bug651. [Jeremy Bennett]

View File

@ -1920,16 +1920,12 @@ uwire keyword.
=head2 SystemVerilog 2005 (IEEE 1800-2005) Support
Verilator currently has some support for SystemVerilog synthesis
constructs. As SystemVerilog features enter common usage they are added;
please file a bug if a feature you need is missing.
Verilator supports ==? and !=? operators, ++ and -- in some contexts,
$bits, $countones, $error, $fatal, $info, $isunknown, $onehot, $onehot0,
$unit, $warning, always_comb, always_ff, always_latch, bit, byte, chandle,
const, do-while, enum, export, final, import, int, logic, longint, package,
program, shortint, struct, time, typedef, union, var, void, priority
case/if, and unique case/if.
const, do-while, enum, export, final, import, int, interface, logic,
longint, modport, package, program, shortint, struct, time, typedef, union,
var, void, priority case/if, and unique case/if.
It also supports .name and .* interconnection.
@ -2605,6 +2601,12 @@ and continue keywords.
Inside expressions may not include unpacked array traversal or $ as an
upper bound. Case inside and case matches are also unsupported.
=item interface
Interfaces and modports, including with generated data types are supported.
Generate blocks around modports are not supported, nor are virtual
interfaces nor unnamed interfaces.
=item priority if, unique if
Priority and unique if's are treated as normal ifs and not asserted to be

View File

@ -410,7 +410,8 @@ public:
BLOCKTEMP,
MODULETEMP,
STMTTEMP,
XTEMP
XTEMP,
IFACEREF // Used to link Interfaces between modules
};
enum en m_e;
inline AstVarType () : m_e(UNKNOWN) {}
@ -424,7 +425,8 @@ public:
"SUPPLY0","SUPPLY1","WIRE","IMPLICITWIRE",
"TRIWIRE","TRI0","TRI1",
"PORT",
"BLOCKTEMP","MODULETEMP","STMTTEMP","XTEMP"};
"BLOCKTEMP","MODULETEMP","STMTTEMP","XTEMP",
"IFACEREF"};
return names[m_e]; }
bool isSignal() const { return (m_e==WIRE || m_e==IMPLICITWIRE
|| m_e==TRIWIRE
@ -1850,4 +1852,9 @@ inline int AstNodeArrayDType::lsb() const { return rangep()->lsbConst(); }
inline int AstNodeArrayDType::elementsConst() const { return rangep()->elementsConst(); }
inline VNumRange AstNodeArrayDType::declRange() const { return VNumRange(msb(), lsb(), rangep()->littleEndian()); }
inline void AstIfaceRefDType::cloneRelink() {
if (m_cellp && m_cellp->clonep()) m_cellp = m_cellp->clonep()->castCell();
if (m_ifacep && m_ifacep->clonep()) m_ifacep = m_ifacep->clonep()->castIface();
if (m_modportp && m_modportp->clonep()) m_modportp = m_modportp->clonep()->castModport(); }
#endif // Guard

View File

@ -35,6 +35,17 @@
// Special methods
// We need these here, because the classes they point to aren't defined when we declare the class
const char* AstIfaceRefDType::broken() const {
BROKEN_RTN(m_ifacep && !m_ifacep->brokeExists());
BROKEN_RTN(m_cellp && !m_cellp->brokeExists());
BROKEN_RTN(m_modportp && !m_modportp->brokeExists());
return NULL;
}
AstIface* AstIfaceRefDType::ifaceViaCellp() const {
return ((m_cellp && m_cellp->modp()) ? m_cellp->modp()->castIface() : m_ifacep);
}
const char* AstNodeVarRef::broken() const {
BROKEN_RTN(m_varScopep && !m_varScopep->brokeExists());
BROKEN_RTN(m_varp && !m_varp->brokeExists());
@ -723,12 +734,31 @@ void AstEnumItemRef::dump(ostream& str) {
if (itemp()) { itemp()->dump(str); }
else { str<<"UNLINKED"; }
}
void AstIfaceRefDType::dump(ostream& str) {
this->AstNode::dump(str);
if (cellName()!="") { str<<" cell="<<cellName(); }
if (ifaceName()!="") { str<<" if="<<ifaceName(); }
if (modportName()!="") { str<<" mp="<<modportName(); }
if (cellp()) { str<<" -> "; cellp()->dump(str); }
else if (ifacep()) { str<<" -> "; ifacep()->dump(str); }
else { str<<" -> UNLINKED"; }
}
void AstIfaceRefDType::dumpSmall(ostream& str) {
this->AstNodeDType::dumpSmall(str);
str<<"iface";
}
void AstJumpGo::dump(ostream& str) {
this->AstNode::dump(str);
str<<" -> ";
if (labelp()) { labelp()->dump(str); }
else { str<<"%Error:UNLINKED"; }
}
void AstModportVarRef::dump(ostream& str) {
this->AstNode::dump(str);
str<<" "<<varType();
if (varp()) { str<<" -> "; varp()->dump(str); }
else { str<<" -> UNLINKED"; }
}
void AstPin::dump(ostream& str) {
this->AstNode::dump(str);
if (modVarp()) { str<<" -> "; modVarp()->dump(str); }

View File

@ -432,6 +432,48 @@ public:
virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); }
};
struct AstIfaceRefDType : public AstNodeDType {
// Reference to an interface, either for a port, or inside parent cell
private:
string m_cellName; // "" = no cell, such as when connects to 'input' iface
string m_ifaceName; // Interface name
string m_modportName; // "" = no modport
AstIface* m_ifacep; // Pointer to interface; note cellp() should override
AstCell* m_cellp; // When exact parent cell known; not a guess
AstModport* m_modportp; // NULL = unlinked or no modport
public:
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName)
: AstNodeDType(fl), m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(""),
m_ifacep(NULL), m_cellp(NULL), m_modportp(NULL) { }
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName, const string& modport)
: AstNodeDType(fl), m_cellName(cellName), m_ifaceName(ifaceName), m_modportName(modport),
m_ifacep(NULL), m_cellp(NULL), m_modportp(NULL) { }
ASTNODE_NODE_FUNCS(IfaceRefDType, IFACEREFDTYPE)
// METHODS
virtual const char* broken() const;
virtual void dump(ostream& str=cout);
virtual void dumpSmall(ostream& str);
virtual void cloneRelink();
virtual AstBasicDType* basicp() const { return NULL; }
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const { return 1; }
virtual int widthTotalBytes() const { return 1; }
string cellName() const { return m_cellName; }
void cellName(const string& name) { m_cellName=name; }
string ifaceName() const { return m_ifaceName; }
void ifaceName(const string& name) { m_ifaceName=name; }
string modportName() const { return m_modportName; }
void modportName(const string& name) { m_modportName=name; }
AstIface* ifaceViaCellp() const; // Use cellp or ifacep
AstIface* ifacep() const { return m_ifacep; }
void ifacep(AstIface* nodep) { m_ifacep=nodep; }
AstCell* cellp() const { return m_cellp; }
void cellp(AstCell* nodep) { m_cellp=nodep; }
AstModport* modportp() const { return m_modportp; }
void modportp(AstModport* modportp) { m_modportp=modportp; }
bool isModport() { return !m_modportName.empty(); }
};
struct AstRefDType : public AstNodeDType {
private:
AstNodeDType* m_refDTypep; // data type pointed to, BELOW the AstTypedef
@ -843,6 +885,7 @@ private:
bool m_isStatic:1; // Static variable
bool m_isPulldown:1; // Tri0
bool m_isPullup:1; // Tri1
bool m_isIfaceParent:1; // dtype is reference to interface present in this module
bool m_trace:1; // Trace this variable
void init() {
@ -854,6 +897,7 @@ private:
m_funcLocal=false; m_funcReturn=false;
m_attrClockEn=false; m_attrScBv=false; m_attrIsolateAssign=false; m_attrSFormat=false;
m_fileDescr=false; m_isConst=false; m_isStatic=false; m_isPulldown=false; m_isPullup=false;
m_isIfaceParent=false;
m_trace=false;
}
public:
@ -944,6 +988,7 @@ public:
void primaryIO(bool flag) { m_primaryIO = flag; }
void isConst(bool flag) { m_isConst = flag; }
void isStatic(bool flag) { m_isStatic = flag; }
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
void funcLocal(bool flag) { m_funcLocal = flag; }
void funcReturn(bool flag) { m_funcReturn = flag; }
void trace(bool flag) { m_trace=flag; }
@ -959,6 +1004,8 @@ public:
bool isPrimaryIO() const { return m_primaryIO; }
bool isPrimaryIn() const { return isPrimaryIO() && isInput(); }
bool isIO() const { return (m_input||m_output); }
bool isIfaceRef() const { return (varType()==AstVarType::IFACEREF); }
bool isIfaceParent() const { return m_isIfaceParent; }
bool isSignal() const { return varType().isSignal(); }
bool isTemp() const { return (varType()==AstVarType::BLOCKTEMP || varType()==AstVarType::MODULETEMP
|| varType()==AstVarType::STMTTEMP || varType()==AstVarType::XTEMP); }
@ -1298,6 +1345,49 @@ public:
void packagep(AstPackage* nodep) { m_packagep=nodep; }
};
struct AstIface : public AstNodeModule {
// A module declaration
AstIface(FileLine* fl, const string& name)
: AstNodeModule (fl,name) { }
ASTNODE_NODE_FUNCS(Iface, IFACE)
};
struct AstModportVarRef : public AstNode {
// A input/output/etc variable referenced under a modport
// The storage for the variable itself is inside the interface, thus this is a reference
// PARENT: AstIface
private:
string m_name; // Name of the variable referenced
AstVarType m_type; // Type of the variable (in/out)
AstVar* m_varp; // Link to the actual Var
public:
AstModportVarRef(FileLine* fl, const string& name, AstVarType::en type)
: AstNode(fl), m_name(name), m_type(type), m_varp(NULL) { }
ASTNODE_NODE_FUNCS(ModportVarRef, MODPORTVARREF)
virtual const char* broken() const { BROKEN_RTN(m_varp && !m_varp->brokeExists()); return NULL; }
virtual void dump(ostream& str);
AstVarType varType() const { return m_type; } // * = Type of variable
virtual string name() const { return m_name; }
bool isInput() const { return (varType()==AstVarType::INPUT || varType()==AstVarType::INOUT); }
bool isOutput() const { return (varType()==AstVarType::OUTPUT || varType()==AstVarType::INOUT); }
AstVar* varp() const { return m_varp; } // [After Link] Pointer to variable
void varp(AstVar* varp) { m_varp=varp; }
};
struct AstModport : public AstNode {
// A modport in an interface
private:
string m_name; // Name of the modport
public:
AstModport(FileLine* fl, const string& name, AstModportVarRef* varsp)
: AstNode(fl), m_name(name) {
addNOp1p(varsp); }
virtual string name() const { return m_name; }
virtual bool maybePointedTo() const { return true; }
ASTNODE_NODE_FUNCS(Modport, MODPORT)
AstModportVarRef* varsp() const { return op1p()->castModportVarRef(); } // op1 = List of Vars
};
struct AstCell : public AstNode {
// A instantiation cell or interface call (don't know which until link)
private:
@ -1305,12 +1395,13 @@ private:
string m_origName; // Original name before dot addition
string m_modName; // Module the cell instances
AstNodeModule* m_modp; // [AfterLink] Pointer to module instanced
bool m_hasIfaceVar; // True if a Var has been created for this cell
public:
AstCell(FileLine* fl, const string& instName, const string& modName,
AstPin* pinsp, AstPin* paramsp, AstRange* rangep)
: AstNode(fl)
, m_name(instName), m_origName(instName), m_modName(modName)
, m_modp(NULL) {
, m_modp(NULL), m_hasIfaceVar(false) {
addNOp1p(pinsp); addNOp2p(paramsp); setNOp3p(rangep); }
ASTNODE_NODE_FUNCS(Cell, CELL)
// No cloneRelink, we presume cloneee's want the same module linkages
@ -1331,6 +1422,8 @@ public:
void addPinsp(AstPin* nodep) { addOp1p(nodep); }
void addParamsp(AstPin* nodep) { addOp2p(nodep); }
void modp(AstNodeModule* nodep) { m_modp = nodep; }
bool hasIfaceVar() const { return m_hasIfaceVar; }
void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; }
};
struct AstCellInline : public AstNode {
@ -1713,6 +1806,16 @@ struct AstAssignW : public AstNodeAssign {
}
};
struct AstAssignVarScope : public AstNodeAssign {
// Assign two VarScopes to each other
AstAssignVarScope(FileLine* fileline, AstNode* lhsp, AstNode* rhsp)
: AstNodeAssign(fileline, lhsp, rhsp) {
dtypeFrom(rhsp);
}
ASTNODE_NODE_FUNCS(AssignVarScope, ASSIGNVARSCOPE)
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) { return new AstAssignVarScope(this->fileline(), lhsp, rhsp); }
};
struct AstPull : public AstNode {
private:
bool m_direction;

View File

@ -1466,6 +1466,9 @@ private:
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
// Don't perform any optimizations, keep the alias around
}
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
// Don't perform any optimizations, the node won't be linked yet
}
virtual void visit(AstAssignW* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (m_doNConst && replaceNodeAssign(nodep)) return;

View File

@ -95,6 +95,11 @@ private:
m_stmtCnt = 0;
m_modp = nodep;
m_modp->user2(CIL_MAYBE);
if (m_modp->castIface()) {
// Inlining an interface means we no longer have a cell handle to resolve to.
// If inlining moves post-scope this can perhaps be relaxed.
cantInline("modIface",true);
}
if (m_modp->modPublic()) cantInline("modPublic",false);
//
nodep->iterateChildren(*this);
@ -146,6 +151,14 @@ private:
// Cleanup link until V3LinkDot can correct it
nodep->varp(NULL);
}
virtual void visit(AstVar* nodep, AstNUser*) {
// Can't look at AstIfaceRefDType directly as it is no longer underneath the module
if (nodep->isIfaceRef()) {
// Unsupported: Inlining of modules with ifaces (see AstIface comment above)
if (m_modp) cantInline("Interfaced",true);
}
nodep->iterateChildren(*this);
}
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
// Cleanup link until V3LinkDot can correct it
if (!nodep->packagep()) nodep->taskp(NULL);
@ -185,6 +198,41 @@ public:
}
};
//######################################################################
// Using clonep(), find cell cross references.
// clone() must not be called inside this visitor
class InlineCollectVisitor : public AstNVisitor {
private:
// NODE STATE
// Output:
// AstCell::user4p() // AstCell* of the created clone
static int debug() {
static int level = -1;
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
return level;
}
// VISITORS
virtual void visit(AstCell* nodep, AstNUser*) {
nodep->user4p(nodep->clonep());
}
// Accelerate
virtual void visit(AstNodeStmt* nodep, AstNUser*) {}
virtual void visit(AstNodeMath* nodep, AstNUser*) {}
virtual void visit(AstNode* nodep, AstNUser*) {
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
InlineCollectVisitor(AstNodeModule* nodep) { // passed OLD module, not new one
nodep->accept(*this);
}
virtual ~InlineCollectVisitor() {}
};
//######################################################################
// After cell is cloned, relink the new module's contents
@ -245,6 +293,13 @@ private:
m_modp->addStmtp(new AstAssignW(nodep->fileline(),
new AstVarRef(nodep->fileline(), exprvarrefp->varp(), true),
new AstVarRef(nodep->fileline(), nodep, false)));
} else if (nodep->isIfaceRef()) {
m_modp->addStmtp(new AstAssignVarScope(nodep->fileline(),
new AstVarRef(nodep->fileline(), nodep, true),
new AstVarRef(nodep->fileline(), exprvarrefp->varp(), false)));
AstNode* nodebp=exprvarrefp->varp();
nodep ->fileline()->modifyStateInherit(nodebp->fileline());
nodebp->fileline()->modifyStateInherit(nodep ->fileline());
} else {
// Do to inlining child's variable now within the same module, so a AstVarRef not AstVarXRef below
m_modp->addStmtp(new AstAssignAlias(nodep->fileline(),
@ -261,7 +316,17 @@ private:
if (!nodep->isFuncLocal()) nodep->inlineAttrReset(name);
if (debug()>=9) { nodep->dumpTree(cout,"varchanged:"); }
if (debug()>=9) { nodep->valuep()->dumpTree(cout,"varchangei:"); }
if (nodep) nodep->iterateChildren(*this);
// Iterate won't hit AstIfaceRefDType directly as it is no longer underneath the module
if (AstIfaceRefDType* ifacerefp = nodep->dtypep()->castIfaceRefDType()) {
// Relink to point to newly cloned cell
if (ifacerefp->cellp()) {
if (AstCell* newcellp = ifacerefp->cellp()->user4p()->castNode()->castCell()) {
ifacerefp->cellp(newcellp);
ifacerefp->cellName(newcellp->name());
}
}
}
nodep->iterateChildren(*this);
}
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
// Function under the inline cell, need to rename to avoid conflicts
@ -357,6 +422,9 @@ private:
// Cleared each cell
// AstVar::user2p() // AstVarRef*/AstConst* Points to signal this is a direct connect to
// AstVar::user3() // bool Don't alias the user2, keep it as signal
// AstCell::user4 // AstCell* of the created clone
AstUser4InUse m_inuser4;
// STATE
AstNodeModule* m_modp; // Current module
@ -397,8 +465,10 @@ private:
//if (debug()>=9) { nodep->modp()->dumpTree(cout,"oldmod:"); }
AstNodeModule* newmodp = nodep->modp()->cloneTree(false);
if (debug()>=9) { newmodp->dumpTree(cout,"newmod:"); }
// Clear var markings
// Clear var markings and find cell cross references
AstNode::user2ClearTree();
AstNode::user4ClearTree();
{ InlineCollectVisitor(nodep->modp()); } // {} to destroy visitor immediately
// Create data for dotted variable resolution
AstCellInline* inlinep = new AstCellInline(nodep->fileline(),
nodep->name(), nodep->modp()->origName());

View File

@ -105,6 +105,13 @@ private:
exprp);
m_modp->addStmtp(assp);
if (debug()>=9) assp->dumpTree(cout," _new: ");
} else if (nodep->modVarp()->isIfaceRef()) {
// Create an AstAssignVarScope for Vars to Cells so we can link with their scope later
AstNode* lhsp = new AstVarXRef (exprp->fileline(), nodep->modVarp(), m_cellp->name(), false);
AstVarRef* refp = exprp->castVarRef();
if (!refp) exprp->v3fatalSrc("Interfaces: Pin is not connected to a VarRef");
AstAssignVarScope* assp = new AstAssignVarScope(exprp->fileline(), lhsp, refp);
m_modp->addStmtp(assp);
} else {
nodep->v3error("Assigned pin is neither input nor output");
}
@ -252,7 +259,11 @@ AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModu
&& connectRefp
&& connectRefp->varp()->dtypep()->sameTree(pinVarp->dtypep())
&& !connectRefp->varp()->isSc()) { // Need the signal as a 'shell' to convert types
// Done. Same data type
// Done. Same data type
} else if (!alwaysCvt
&& connectRefp
&& connectRefp->varp()->isIfaceRef()) {
// Done. Interface
} else if (!alwaysCvt
&& connBasicp
&& pinBasicp

View File

@ -190,6 +190,7 @@ private:
<<"' does not match "<<nodep->typeName()<<" name: "<<nodep->prettyName());
}
}
if (nodep->castIface() || nodep->castPackage()) nodep->inLibrary(true); // Interfaces can't be at top, unless asked
bool topMatch = (v3Global.opt.topModule()==nodep->prettyName());
if (topMatch) {
m_topVertexp = vertex(nodep);
@ -210,6 +211,25 @@ private:
m_modp = NULL;
}
virtual void visit(AstIfaceRefDType* nodep, AstNUser*) {
// Cell: Resolve its filename. If necessary, parse it.
UINFO(4,"Link IfaceRef: "<<nodep<<endl);
// Use findIdUpward instead of findIdFlat; it doesn't matter for now
// but we might support modules-under-modules someday.
AstNodeModule* modp = resolveModule(nodep, nodep->ifaceName());
if (modp) {
if (modp->castIface()) {
// Track module depths, so can sort list from parent down to children
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false);
if (!nodep->cellp()) nodep->ifacep(modp->castIface());
} else if (modp->castNotFoundModule()) { // Will error out later
} else {
nodep->v3error("Non-interface used as an interface: "<<nodep->prettyName());
}
}
// Note cannot do modport resolution here; modports are allowed underneath generates
}
virtual void visit(AstPackageImport* nodep, AstNUser*) {
// Package Import: We need to do the package before the use of a package
nodep->iterateChildren(*this);
@ -314,6 +334,23 @@ private:
}
}
}
if (nodep->modp()->castIface()) {
// Cell really is the parent's instantiation of an interface, not a normal module
// Make sure we have a variable to refer to this cell, so can <ifacename>.<innermember>
// in the same way that a child does. Rename though to avoid conflict with cell.
// This is quite similar to how classes work; when unpacked classes are better supported
// may remap interfaces to be more like a class.
if (!nodep->hasIfaceVar()) {
string varName = nodep->name()+"__Viftop"; // V3LinkDot looks for this naming
AstIfaceRefDType* idtypep = new AstIfaceRefDType(nodep->fileline(), nodep->name(), nodep->modp()->name());
idtypep->cellp(nodep); // Only set when real parent cell known
idtypep->ifacep(NULL); // cellp overrides
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep);
varp->isIfaceParent(true);
nodep->addNextHere(varp);
nodep->hasIfaceVar(true);
}
}
if (nodep->modp()) {
nodep->iterateChildren(*this);
}

View File

@ -31,6 +31,26 @@
// VarXRef/Func's:
// Find appropriate named cell and link to var they reference
//*************************************************************************
// Interfaces:
// CELL (.port (ifref)
// ^--- cell -> IfaceDTypeRef(iface)
// ^--- cell.modport -> IfaceDTypeRef(iface,modport)
// ^--- varref(input_ifref) -> IfaceDTypeRef(iface)
// ^--- varref(input_ifref).modport -> IfaceDTypeRef(iface,modport)
// FindVisitor:
// #1: Insert interface Vars
// #2: Insert ModPort names
// IfaceVisitor:
// #3: Update ModPortVarRef to point at interface vars (after #1)
// #4: Create ModPortVarRef symbol table entries
// FindVisitor-insertIfaceRefs()
// #5: Resolve IfaceRefDtype modport names (after #2)
// #7: Record sym of IfaceRefDType and aliased interface and/or modport (after #4,#5)
// insertAllScopeAliases():
// #8: Insert modport's symbols under IfaceRefDType (after #7)
// ResolveVisitor:
// #9: Resolve general variables, which may point into the interface or modport (after #8)
//*************************************************************************
// TOP
// {name-of-top-modulename}
// a (VSymEnt->AstCell)
@ -77,8 +97,11 @@ private:
AstUser4InUse m_inuser4;
// TYPES
typedef std::multimap<string,VSymEnt*> NameScopeSymMap;
typedef set <pair<AstNodeModule*,string> > ImplicitNameSet;
typedef multimap<string,VSymEnt*> NameScopeSymMap;
typedef map<VSymEnt*,VSymEnt*> ScopeAliasMap;
typedef set<pair<AstNodeModule*,string> > ImplicitNameSet;
typedef vector<VSymEnt*> IfaceVarSyms;
typedef vector<pair<AstIface*,VSymEnt*> > IfaceModSyms;
static LinkDotState* s_errorThisp; // Last self, for error reporting only
@ -87,6 +110,9 @@ private:
VSymEnt* m_dunitEntp; // $unit entry
NameScopeSymMap m_nameScopeSymMap; // Map of scope referenced by non-pretty textual name
ImplicitNameSet m_implicitNameSet; // For [module][signalname] if we can implicitly create it
ScopeAliasMap m_scopeAliasMap; // Map of <lhs,rhs> aliases
IfaceVarSyms m_ifaceVarSyms; // List of AstIfaceRefDType's to be imported
IfaceModSyms m_ifaceModSyms; // List of AstIface+Symbols to be processed
bool m_forPrimary; // First link
bool m_forPrearray; // Compress cell__[array] refs
bool m_forScopeCreation; // Remove VarXRefs for V3Scope
@ -105,6 +131,10 @@ public:
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
ostream& os = *logp;
m_syms.dump(os);
if (!m_scopeAliasMap.empty()) os<<"\nScopeAliasMap:\n";
for (ScopeAliasMap::iterator it = m_scopeAliasMap.begin(); it != m_scopeAliasMap.end(); ++it) {
os<<"\t"<<it->first<<" -> "<<it->second<<endl;
}
}
}
static void preErrorDumpHandler() {
@ -148,6 +178,7 @@ public:
else if (nodep->castTask()) return "task";
else if (nodep->castFunc()) return "function";
else if (nodep->castBegin()) return "block";
else if (nodep->castIface()) return "interface";
else return nodep->prettyTypeName();
}
static string ucfirst(const string& text) {
@ -314,6 +345,69 @@ public:
&& (m_implicitNameSet.find(make_pair(nodep,varname)) != m_implicitNameSet.end());
}
// Track and later recurse interface modules
void insertIfaceModSym(AstIface* nodep, VSymEnt* symp) {
m_ifaceModSyms.push_back(make_pair(nodep, symp));
}
void computeIfaceModSyms();
// Track and later insert interface references
void insertIfaceVarSym(VSymEnt* symp) { // Where sym is for a VAR of dtype IFACEREFDTYPE
m_ifaceVarSyms.push_back(symp);
}
void computeIfaceVarSyms() {
for (IfaceVarSyms::iterator it = m_ifaceVarSyms.begin(); it != m_ifaceVarSyms.end(); ++it) {
VSymEnt* varSymp = *it;
AstVar* varp = varSymp->nodep()->castVar();
UINFO(9, " insAllIface se"<<(void*)varSymp<<" "<<varp<<endl);
AstIfaceRefDType* ifacerefp = varp->subDTypep()->castIfaceRefDType();
if (!ifacerefp) varp->v3fatalSrc("Non-ifacerefs on list!");
if (!ifacerefp->ifaceViaCellp()) ifacerefp->v3fatalSrc("Unlinked interface");
VSymEnt* ifaceSymp = getNodeSym(ifacerefp->ifaceViaCellp());
VSymEnt* ifOrPortSymp = ifaceSymp;
// Link Modport names to the Modport Node under the Interface
if (ifacerefp->isModport()) {
VSymEnt* foundp = ifaceSymp->findIdFallback(ifacerefp->modportName());
bool ok = false;
if (foundp) {
if (AstModport* modportp = foundp->nodep()->castModport()) {
UINFO(4,"Link Modport: "<<modportp<<endl);
ifacerefp->modportp(modportp);
ifOrPortSymp = foundp;
ok = true;
}
}
if (!ok) ifacerefp->v3error("Modport not found under interface '"
<<ifacerefp->prettyName(ifacerefp->ifaceName())
<<"': "<<ifacerefp->prettyName(ifacerefp->modportName()));
}
// Alias won't expand until interfaces and modport names are known; see notes at top
insertScopeAlias(varSymp, ifOrPortSymp);
}
m_ifaceVarSyms.clear();
}
// Track and later insert scope aliases
void insertScopeAlias(VSymEnt* lhsp, VSymEnt* rhsp) { // Typically lhsp=VAR w/dtype IFACEREF, rhsp=IFACE cell
UINFO(9," insertScopeAlias se"<<(void*)lhsp<<" se"<<(void*)rhsp<<endl);
m_scopeAliasMap.insert(make_pair(lhsp, rhsp));
}
void computeScopeAliases() {
UINFO(9,"computeIfaceAliases\n");
for (ScopeAliasMap::iterator it=m_scopeAliasMap.begin(); it!=m_scopeAliasMap.end(); ++it) {
VSymEnt* lhsp = it->first;
VSymEnt* srcp = lhsp;
while (1) { // Follow chain of aliases up to highest level non-alias
ScopeAliasMap::iterator it2 = m_scopeAliasMap.find(srcp);
if (it2 != m_scopeAliasMap.end()) { srcp = it2->second; continue; }
else break;
}
UINFO(9," iiasa: Insert alias se"<<lhsp<<" <- se"<<srcp<<" "<<srcp->nodep()<<endl);
// srcp should be an interface reference pointing to the interface we want to import
lhsp->importFromIface(symsp(), srcp);
}
m_scopeAliasMap.clear();
}
private:
VSymEnt* findWithAltFallback(VSymEnt* symp, const string& name, const string& altname) {
VSymEnt* findp = symp->findIdFallback(name);
@ -503,6 +597,10 @@ class LinkDotFindVisitor : public AstNVisitor {
// Iterate
nodep->iterateChildren(*this);
nodep->user4(true);
// Interfaces need another pass when signals are resolved
if (AstIface* ifacep = nodep->castIface()) {
m_statep->insertIfaceModSym(ifacep, m_curSymp);
}
} else { //!doit
// Will be optimized away later
// Can't remove now, as our backwards iterator will throw up
@ -719,12 +817,16 @@ class LinkDotFindVisitor : public AstNVisitor {
}
}
if (ins) {
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
VSymEnt* insp = m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
if (m_statep->forPrimary() && nodep->isGParam()) {
m_paramNum++;
VSymEnt* symp = m_statep->insertSym(m_curSymp, "__paramNumber"+cvtToStr(m_paramNum), nodep, m_packagep);
symp->exported(false);
}
if (nodep->subDTypep()->castIfaceRefDType()) {
// Can't resolve until interfaces and modport names are known; see notes at top
m_statep->insertIfaceVarSym(insp);
}
}
}
}
@ -899,8 +1001,8 @@ private:
AstVar* refp = foundp->nodep()->castVar();
if (!refp) {
nodep->v3error("Input/output/inout declaration not found for port: "<<nodep->prettyName());
} else if (!refp->isIO()) {
nodep->v3error("Pin is not an in/out/inout: "<<nodep->prettyName());
} else if (!refp->isIO() && !refp->isIfaceRef()) {
nodep->v3error("Pin is not an in/out/inout/interface: "<<nodep->prettyName());
} else {
refp->user4(true);
VSymEnt* symp = m_statep->insertSym(m_statep->getNodeSym(m_modp),
@ -956,9 +1058,10 @@ public:
//======================================================================
class LinkDotScopeVisitor : public AstNVisitor {
private:
// STATE
LinkDotState* m_statep; // State to pass between visitors, including symbol table
AstScope* m_scopep; // The current scope
VSymEnt* m_modSymp; // Symbol entry for current module
int debug() { return LinkDotState::debug(); }
@ -974,12 +1077,36 @@ private:
// Using the CELL names, we created all hierarchy. We now need to match this Scope
// up with the hierarchy created by the CELL names.
m_modSymp = m_statep->getScopeSym(nodep);
m_scopep = nodep;
nodep->iterateChildren(*this);
m_modSymp = NULL;
m_scopep = NULL;
}
virtual void visit(AstVarScope* nodep, AstNUser*) {
if (!nodep->varp()->isFuncLocal()) {
m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep, NULL);
VSymEnt* varSymp = m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep, NULL);
if (nodep->varp()->isIfaceRef()
&& nodep->varp()->isIfaceParent()) {
UINFO(9,"Iface parent ref var "<<nodep->varp()->name()<<" "<<nodep<<endl);
// Find the interface cell the var references
AstIfaceRefDType* dtypep = nodep->varp()->dtypep()->castIfaceRefDType();
if (!dtypep) nodep->v3fatalSrc("Non AstIfaceRefDType on isIfaceRef() var");
UINFO(9,"Iface parent dtype "<<dtypep<<endl);
string ifcellname = dtypep->cellName();
string baddot; VSymEnt* okSymp;
VSymEnt* cellSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp);
if (!cellSymp) nodep->v3fatalSrc("No symbol for interface cell: " <<nodep->prettyName(ifcellname));
UINFO(5, " Found interface cell: se"<<(void*)cellSymp<<" "<<cellSymp->nodep()<<endl);
if (dtypep->modportName()!="") {
VSymEnt* mpSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp);
if (!mpSymp) { nodep->v3fatalSrc("No symbol for interface modport: " <<nodep->prettyName(dtypep->modportName())); }
else cellSymp = mpSymp;
UINFO(5, " Found modport cell: se"<<(void*)cellSymp<<" "<<mpSymp->nodep()<<endl);
}
// Interface reference; need to put whole thing into symtable, but can't clone it now
// as we may have a later alias for it.
m_statep->insertScopeAlias(varSymp, cellSymp);
}
}
}
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
@ -997,6 +1124,37 @@ private:
fromVscp->user2p(toVscp);
nodep->iterateChildren(*this);
}
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
UINFO(5,"ASSIGNVARSCOPE "<<nodep<<endl);
if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\tavs: ");
VSymEnt* rhsSymp;
{
AstVarRef* refp = nodep->rhsp()->castVarRef();
if (!refp) nodep->v3fatalSrc("Unsupported: Non VarRef attached to interface pin");
string scopename = refp->name();
string baddot; VSymEnt* okSymp;
VSymEnt* symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp);
if (!symp) nodep->v3fatalSrc("No symbol for interface alias rhs");
UINFO(5, " Found a linked scope RHS: "<<scopename<<" se"<<(void*)symp<<" "<<symp->nodep()<<endl);
rhsSymp = symp;
}
VSymEnt* lhsSymp;
{
AstVarXRef* refp = nodep->lhsp()->castVarXRef();
if (!refp) nodep->v3fatalSrc("Unsupported: Non VarXRef attached to interface pin");
string scopename = refp->dotted()+"."+refp->name();
string baddot; VSymEnt* okSymp;
VSymEnt* symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp);
if (!symp) nodep->v3fatalSrc("No symbol for interface alias lhs");
UINFO(5, " Found a linked scope LHS: "<<scopename<<" se"<<(void*)symp<<" "<<symp->nodep()<<endl);
lhsSymp = symp;
}
// Remember the alias - can't do it yet because we may have additional symbols to be added,
// or maybe an alias of an alias
m_statep->insertScopeAlias(lhsSymp, rhsSymp);
// We have stored the link, we don't need these any more
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
}
// For speed, don't recurse things that can't have scope
// Note we allow AstNodeStmt's as generates may be under them
virtual void visit(AstCell*, AstNUser*) {}
@ -1012,6 +1170,7 @@ public:
LinkDotScopeVisitor(AstNetlist* rootp, LinkDotState* statep) {
UINFO(4,__FUNCTION__<<": "<<endl);
m_modSymp = NULL;
m_scopep = NULL;
m_statep = statep;
//
rootp->accept(*this);
@ -1021,6 +1180,78 @@ public:
//======================================================================
// Iterate an interface to resolve modports
class LinkDotIfaceVisitor : public AstNVisitor {
// STATE
LinkDotState* m_statep; // State to pass between visitors, including symbol table
VSymEnt* m_curSymp; // Symbol Entry for current table, where to lookup/insert
// METHODS
int debug() { return LinkDotState::debug(); }
// VISITs
virtual void visit(AstModport* nodep, AstNUser*) {
// Modport: Remember its name for later resolution
UINFO(5," fiv: "<<nodep<<endl);
VSymEnt* oldCurSymp = m_curSymp;
{
// Create symbol table for the vars
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, NULL);
m_curSymp->fallbackp(oldCurSymp);
nodep->iterateChildren(*this);
}
m_curSymp = oldCurSymp;
}
virtual void visit(AstModportVarRef* nodep, AstNUser*) {
UINFO(5," fiv: "<<nodep<<endl);
nodep->iterateChildren(*this);
VSymEnt* symp = m_curSymp->findIdFallback(nodep->name());
if (!symp) {
nodep->v3error("Modport item not found: "<<nodep->prettyName());
} else if (AstVar* varp = symp->nodep()->castVar()) {
// Make symbol under modport that points at the _interface_'s var, not the modport.
nodep->varp(varp);
m_statep->insertSym(m_curSymp, nodep->name(), varp, NULL/*package*/);
} else if (AstVarScope* vscp = symp->nodep()->castVarScope()) {
// 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, NULL/*package*/);
} else {
nodep->v3error("Modport item is not a variable: "<<nodep->prettyName());
}
if (m_statep->forScopeCreation()) {
// Done with AstModportVarRef.
// Delete to prevent problems if we dead-delete pointed to variable
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
}
}
virtual void visit(AstNode* nodep, AstNUser*) {
// Default: Just iterate
nodep->iterateChildren(*this);
}
public:
// CONSTUCTORS
LinkDotIfaceVisitor(AstIface* nodep, VSymEnt* curSymp, LinkDotState* statep) {
UINFO(4,__FUNCTION__<<": "<<endl);
m_curSymp = curSymp;
m_statep = statep;
nodep->accept(*this);
}
virtual ~LinkDotIfaceVisitor() {}
};
void LinkDotState::computeIfaceModSyms() {
for (IfaceModSyms::iterator it=m_ifaceModSyms.begin(); it!=m_ifaceModSyms.end(); ++it) {
AstIface* nodep = it->first;
VSymEnt* symp = it->second;
LinkDotIfaceVisitor(nodep, symp, this);
}
m_ifaceModSyms.clear();
}
//======================================================================
class LinkDotResolveVisitor : public AstNVisitor {
private:
// NODE STATE
@ -1049,6 +1280,7 @@ private:
AstCell* m_cellp; // Current cell
AstNodeModule* m_modp; // Current module
AstNodeFTask* m_ftaskp; // Current function/task
int m_modportNum; // Uniqueify modport numbers
struct DotStates {
DotPosition m_dotPos; // Scope part of dotted resolution
@ -1104,6 +1336,16 @@ private:
m_ds.m_dotErr = true;
}
}
AstVar* makeIfaceModportVar(FileLine* fl, AstCell* cellp, AstIface* ifacep, AstModport* modportp) {
// Create iface variable, using duplicate var when under same module scope
string varName = ifacep->name()+"__Vmp__"+modportp->name()+"__Viftop"+cvtToStr(++m_modportNum);
AstIfaceRefDType* idtypep = new AstIfaceRefDType(fl, cellp->name(), ifacep->name(), modportp->name());
idtypep->cellp(cellp);
AstVar* varp = new AstVar(fl, AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep);
varp->isIfaceParent(true);
m_modp->addStmtp(varp);
return varp;
}
// VISITs
virtual void visit(AstNetlist* nodep, AstNUser* vup) {
@ -1119,6 +1361,7 @@ private:
m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getNodeSym(nodep); // Until overridden by a SCOPE
m_cellp = NULL;
m_modp = nodep;
m_modportNum = 0;
nodep->iterateChildren(*this);
m_modp = NULL;
m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL;
@ -1183,8 +1426,8 @@ private:
return;
}
nodep->v3error("Pin not found: "<<nodep->prettyName());
} else if (!refp->isIO() && !refp->isParam()) {
nodep->v3error("Pin is not an in/out/inout/param: "<<nodep->prettyName());
} else if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) {
nodep->v3error("Pin is not an in/out/inout/param/interface: "<<nodep->prettyName());
} else {
nodep->modVarp(refp);
if (refp->user5p() && refp->user5p()->castNode()!=nodep) {
@ -1326,9 +1569,41 @@ private:
m_ds.m_dotPos = DP_SCOPE;
// Upper AstDot visitor will handle it from here
}
else if (foundp->nodep()->castCell()
&& allowVar && m_cellp
&& foundp->nodep()->castCell()->modp()->castIface()) {
// Interfaces can be referenced like a variable for interconnect
AstCell* cellp = foundp->nodep()->castCell();
VSymEnt* cellEntp = m_statep->getNodeSym(cellp); if (!cellEntp) nodep->v3fatalSrc("No interface sym entry");
VSymEnt* parentEntp = cellEntp->parentp(); // Container of the var; probably a module or generate begin
string findName = nodep->name()+"__Viftop";
AstVar* ifaceRefVarp = parentEntp->findIdFallback(findName)->nodep()->castVar();
if (!ifaceRefVarp) nodep->v3fatalSrc("Can't find interface var ref: "<<findName);
//
ok = true;
if (m_ds.m_dotText!="") m_ds.m_dotText += ".";
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, false);
nodep->replaceWith(newp); pushDeletep(nodep); nodep = NULL;
}
}
else if (AstVar* varp = foundp->nodep()->castVar()) {
if (allowVar) {
if (AstIfaceRefDType* ifacerefp = varp->subDTypep()->castIfaceRefDType()) {
if (!ifacerefp->ifaceViaCellp()) ifacerefp->v3fatalSrc("Unlinked interface");
// Really this is a scope reference into an interface
UINFO(9,"varref-ifaceref "<<m_ds.m_dotText<<" "<<nodep<<endl);
if (m_ds.m_dotText!="") m_ds.m_dotText += ".";
m_ds.m_dotText += nodep->name();
m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->ifaceViaCellp());
m_ds.m_dotPos = DP_SCOPE;
ok = true;
AstNode* newp = new AstVarRef(nodep->fileline(), varp, false);
nodep->replaceWith(newp); pushDeletep(nodep); nodep = NULL;
}
else if (allowVar) {
AstNodeVarRef* newp;
if (m_ds.m_dotText != "") {
newp = new AstVarXRef(nodep->fileline(), nodep->name(), m_ds.m_dotText, false); // lvalue'ness computed later
@ -1345,6 +1620,34 @@ private:
ok = true;
}
}
else if (AstModport* modportp = foundp->nodep()->castModport()) {
// A scope reference into an interface's modport (not necessarily at a pin connection)
UINFO(9,"cell-ref-to-modport "<<m_ds.m_dotText<<" "<<nodep<<endl);
UINFO(9,"dotSymp "<<m_ds.m_dotSymp<<" "<<m_ds.m_dotSymp->nodep()<<endl);
// Iface was the previously dotted component
if (!m_ds.m_dotSymp
|| !m_ds.m_dotSymp->nodep()->castCell()
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) {
nodep->v3error("Modport not referenced as <interface>."<<modportp->prettyName());
} else if (!m_ds.m_dotSymp->nodep()->castCell()->modp()
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) {
nodep->v3error("Modport not referenced from underneath an interface: "<<modportp->prettyName());
} else {
AstCell* cellp = m_ds.m_dotSymp->nodep()->castCell();
if (!cellp) nodep->v3fatalSrc("Modport not referenced from a cell");
AstIface* ifacep = cellp->modp()->castIface();
//string cellName = m_ds.m_dotText; // Use cellp->name
if (m_ds.m_dotText!="") m_ds.m_dotText += ".";
m_ds.m_dotText += nodep->name();
m_ds.m_dotSymp = m_statep->getNodeSym(modportp);
m_ds.m_dotPos = DP_SCOPE;
ok = true;
AstVar* varp = makeIfaceModportVar(nodep->fileline(), cellp, ifacep, modportp);
AstVarRef* refp = new AstVarRef(varp->fileline(), varp, false);
nodep->replaceWith(refp); pushDeletep(nodep); nodep = NULL;
}
}
else if (AstEnumItem* valuep = foundp->nodep()->castEnumItem()) {
if (allowVar) {
AstNode* newp = new AstEnumItemRef(nodep->fileline(), valuep, foundp->packagep());
@ -1677,6 +1980,7 @@ public:
m_cellp = NULL;
m_modp = NULL;
m_ftaskp = NULL;
m_modportNum = 0;
//
rootp->accept(*this);
}
@ -1698,12 +2002,18 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) {
LinkDotParamVisitor visitors(rootp,&state);
if (LinkDotState::debug()>=5 || v3Global.opt.dumpTree()>=9) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-param.tree"));
}
else if (step == LDS_ARRAYED) {}
else if (step == LDS_SCOPED) {
// Well after the initial link when we're ready to operate on the flat design,
// process AstScope's. This needs to be separate pass after whole hierarchy graph created.
LinkDotScopeVisitor visitors(rootp,&state);
if (LinkDotState::debug()>=5 || v3Global.opt.dumpTree()>=9) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-scoped.tree"));
}
else v3fatalSrc("Bad case");
state.dump();
state.computeIfaceModSyms();
state.computeIfaceVarSyms();
state.computeScopeAliases();
state.dump();
LinkDotResolveVisitor visitorb(rootp,&state);
}

View File

@ -152,6 +152,12 @@ private:
nodep->valuep()->unlinkFrBack()));
}
}
if (nodep->isIfaceRef() && !nodep->isIfaceParent()) {
// Only AstIfaceRefDType's at this point correspond to ports;
// haven't made additional ones for interconnect yet, so assert is simple
// What breaks later is we don't have a Scope/Cell representing the interface to attach to
if (m_modp->level()<=2) nodep->v3error("Unsupported: Interfaced port on top level module");
}
}
virtual void visit(AstAttrOf* nodep, AstNUser*) {

View File

@ -21,13 +21,31 @@
// Top down traversal:
// For each cell:
// If parameterized,
// Determine all parameter widths, constant values
// Determine all parameter widths, constant values.
// (Interfaces also matter, as if an interface is parameterized
// this effectively changes the width behavior of all that
// reference the iface.)
// Clone module cell calls, renaming with __{par1}_{par2}_...
// Substitute constants for cell's module's parameters
// Relink pins and cell to point to new module
// Then process all modules called by that cell
// Substitute constants for cell's module's parameters.
// Relink pins and cell and ifacerefdtype to point to new module.
//
// For interface Parent's we have the AstIfaceRefDType::cellp()
// pointing to this module. If that parent cell's interface
// module gets parameterized, AstIfaceRefDType::cloneRelink
// will update AstIfaceRefDType::cellp(), and AstLinkDot will
// see the new interface.
//
// However if a submodule's AstIfaceRefDType::ifacep() points
// to the old (unparameterized) interface and needs correction.
// To detect this we must walk all pins looking for interfaces
// that the parent has changed and propagate down.
//
// Then process all modules called by that cell.
// (Cells never referenced after parameters expanded must be ignored.)
//
// After we complete parameters, the varp's will be wrong (point to old module)
// and must be relinked.
//
//*************************************************************************
#include "config_build.h"
@ -62,23 +80,34 @@ private:
AstUser5InUse m_inuser5;
// User1/2/3 used by constant function simulations
// TYPES
typedef deque<pair<AstIfaceRefDType*,AstIfaceRefDType*> > IfaceRefRefs; // Note may have duplicate entries
// STATE
typedef std::map<AstVar*,AstVar*> VarCloneMap;
typedef map<AstVar*,AstVar*> VarCloneMap;
struct ModInfo {
AstNodeModule* m_modp; // Module with specified name
VarCloneMap m_cloneMap; // Map of old-varp -> new cloned varp
ModInfo(AstNodeModule* modp) { m_modp=modp; }
};
typedef std::map<string,ModInfo> ModNameMap;
typedef map<string,ModInfo> ModNameMap;
ModNameMap m_modNameMap; // Hash of created module flavors by name
typedef std::map<string,string> LongMap;
typedef map<string,string> LongMap;
LongMap m_longMap; // Hash of very long names to unique identity number
int m_longId;
typedef map<AstNode*,int> ValueMap;
typedef map<int,int> NextValueMap;
ValueMap m_valueMap; // Hash of node to param value
NextValueMap m_nextValueMap;// Hash of param value to next value to be used
typedef multimap<int,AstNodeModule*> LevelModMap;
LevelModMap m_todoModps; // Modules left to process
typedef deque<AstCell*> CellList;
CellList m_cellps; // Cells left to process (in this module)
// METHODS
static int debug() {
static int level = -1;
@ -91,7 +120,7 @@ private:
// Pass 1, assign first letter to each gparam's name
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
if (AstVar* varp = stmtp->castVar()) {
if (varp->isGParam()) {
if (varp->isGParam()||varp->isIfaceRef()) {
char ch = varp->name()[0];
ch = toupper(ch); if (ch<'A' || ch>'Z') ch='Z';
varp->user4(usedLetter[static_cast<int>(ch)]*256 + ch);
@ -113,6 +142,27 @@ private:
}
return st;
}
string paramValueNumber(AstNode* nodep) {
// Given a compilcated object create a number to use for param module assignment
// Ideally would be relatively stable if design changes (not use pointer value),
// and must return same value given same input node
// Return must presently be numberic so doesn't collide with 'small' alphanumeric parameter names
ValueMap::iterator it = m_valueMap.find(nodep);
if (it != m_valueMap.end()) {
return cvtToStr(it->second);
} else {
static int BUCKETS = 1000;
V3Hash hash (nodep->name());
int bucket = hash.hshval() % BUCKETS;
int offset = 0;
NextValueMap::iterator it = m_nextValueMap.find(bucket);
if (it != m_nextValueMap.end()) { offset = it->second; it->second = offset + 1; }
else { m_nextValueMap.insert(make_pair(bucket, offset + 1)); }
int num = bucket + offset * BUCKETS;
m_valueMap.insert(make_pair(nodep, num));
return cvtToStr(num);
}
}
void relinkPins(VarCloneMap* clonemapp, AstPin* startpinp) {
for (AstPin* pinp = startpinp; pinp; pinp=pinp->nextp()->castPin()) {
if (!pinp->modVarp()) pinp->v3fatalSrc("Not linked?\n");
@ -123,9 +173,10 @@ private:
pinp->modVarp(cloneiter->second);
}
}
void visitCell(AstCell* nodep);
void visitModules() {
// Loop on all modules left to process
// Hitting a cell adds to the appropriate leval of this level-sorted list,
// Hitting a cell adds to the appropriate level of this level-sorted list,
// so since cells originally exist top->bottom we process in top->bottom order too.
while (!m_todoModps.empty()) {
LevelModMap::iterator it = m_todoModps.begin();
@ -134,7 +185,19 @@ private:
if (!nodep->user5SetOnce()) { // Process once; note clone() must clear so we do it again
UINFO(4," MOD "<<nodep<<endl);
nodep->iterateChildren(*this);
// Note this may add to m_todoModps
// Note above iterate may add to m_todoModps
//
// Process interface cells, then non-interface which may ref an interface cell
for (int nonIf=0; nonIf<2; ++nonIf) {
for (CellList::iterator it=m_cellps.begin(); it!=m_cellps.end(); ++it) {
AstCell* nodep = *it;
if ((nonIf==0 && nodep->modp()->castIface())
|| (nonIf==1 && !nodep->modp()->castIface())) {
visitCell(nodep);
}
}
}
m_cellps.clear();
}
}
}
@ -157,7 +220,10 @@ private:
UINFO(4," MOD-dead? "<<nodep<<endl); // Should have been done by now, if not dead
}
}
virtual void visit(AstCell* nodep, AstNUser*);
virtual void visit(AstCell* nodep, AstNUser*) {
// Must do ifaces first, so push to list and do in proper order
m_cellps.push_back(nodep);
}
// Make sure all parameters are constantified
virtual void visit(AstVar* nodep, AstNUser*) {
@ -218,7 +284,7 @@ private:
//! Parameter subsitution for generated for loops.
//! @todo Unlike generated IF, we don't have to worry about short-circuiting the conditional
//! expression, since this is currently restricted to simple comparisons. If we ever do
//! move to more generic constant expressions, such code will be neede here.
//! move to more generic constant expressions, such code will be needed here.
virtual void visit(AstBegin* nodep, AstNUser*) {
if (nodep->genforp()) {
AstGenFor* forp = nodep->genforp()->castGenFor();
@ -317,11 +383,13 @@ public:
//----------------------------------------------------------------------
// VISITs
void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
void ParamVisitor::visitCell(AstCell* nodep) {
// Cell: Check for parameters in the instantiation.
nodep->iterateChildren(*this);
if (!nodep->modp()) { nodep->dumpTree(cerr,"error:"); nodep->v3fatalSrc("Not linked?"); }
if (nodep->paramsp()) {
if (!nodep->modp()) nodep->v3fatalSrc("Not linked?");
if (nodep->paramsp()
|| 1 // Need to look for interfaces; could track when one exists, but should be harmless to always do this
) {
UINFO(4,"De-parameterize: "<<nodep<<endl);
// Create new module name with _'s between the constants
if (debug()>=10) nodep->dumpTree(cout,"-cell:\t");
@ -359,6 +427,30 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
}
}
}
IfaceRefRefs ifaceRefRefs;
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
AstVar* modvarp = pinp->modVarp();
if (modvarp->isIfaceRef()) {
AstIfaceRefDType* portIrefp = modvarp->subDTypep()->castIfaceRefDType();
//UINFO(9," portIfaceRef "<<portIrefp<<endl);
if (!pinp->exprp()
|| !pinp->exprp()->castVarRef()
|| !pinp->exprp()->castVarRef()->varp()
|| !pinp->exprp()->castVarRef()->varp()->subDTypep()
|| !pinp->exprp()->castVarRef()->varp()->subDTypep()->castIfaceRefDType()) {
pinp->v3error("Interface port '"<<modvarp->prettyName()<<"' is not connected to interface/modport pin expression");
} else {
AstIfaceRefDType* pinIrefp = pinp->exprp()->castVarRef()->varp()->subDTypep()->castIfaceRefDType();
//UINFO(9," pinIfaceRef "<<pinIrefp<<endl);
if (portIrefp->ifaceViaCellp() != pinIrefp->ifaceViaCellp()) {
UINFO(9," IfaceRefDType needs reconnect "<<pinIrefp<<endl);
longname += "_" + paramSmallName(nodep->modp(),pinp->modVarp())+paramValueNumber(pinIrefp);
any_overrides = true;
ifaceRefRefs.push_back(make_pair(portIrefp,pinIrefp));
}
}
}
}
if (!any_overrides) {
UINFO(8,"Cell parameters all match original values, skipping expansion.\n");
@ -401,7 +493,7 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
// Note we allow multiple users of a parameterized model, thus we need to stash this info.
for (AstNode* stmtp=modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* varp = stmtp->castVar()) {
if (varp->isIO() || varp->isGParam()) {
if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) {
// Cloning saved a pointer to the new node for us, so just follow that link.
AstVar* oldvarp = varp->clonep()->castVar();
//UINFO(8,"Clone list 0x"<<hex<<(uint32_t)oldvarp<<" -> 0x"<<(uint32_t)varp<<endl);
@ -413,7 +505,21 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
// Relink parameter vars to the new module
relinkPins(clonemapp, nodep->paramsp());
// Fix any interface references
for (IfaceRefRefs::iterator it=ifaceRefRefs.begin(); it!=ifaceRefRefs.end(); ++it) {
AstIfaceRefDType* portIrefp = it->first;
AstIfaceRefDType* pinIrefp = it->second;
AstIfaceRefDType* cloneIrefp = portIrefp->clonep()->castIfaceRefDType();
UINFO(8," IfaceOld "<<portIrefp<<endl);
UINFO(8," IfaceTo "<<pinIrefp<<endl);
if (!cloneIrefp) portIrefp->v3fatalSrc("parameter clone didn't hit AstIfaceRefDType");
UINFO(8," IfaceClo "<<cloneIrefp<<endl);
cloneIrefp->ifacep(pinIrefp->ifaceViaCellp());
UINFO(8," IfaceNew "<<cloneIrefp<<endl);
}
// Assign parameters to the constants specified
// DOES clone() so must be finished with module clonep() before here
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) {
AstVar* modvarp = pinp->modVarp();
if (modvarp && pinp->exprp()) {
@ -439,7 +545,7 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
} // if any_overrides
// Delete the parameters from the cell; they're not relevant any longer.
nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
UINFO(8," Done with "<<nodep<<endl);
//if (debug()>=10) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree"));
}

View File

@ -66,6 +66,7 @@ struct V3ParseBisonYYSType {
AstCell* cellp;
AstConst* constp;
AstMemberDType* memberp;
AstModportVarRef* modportvarrefp;
AstNodeModule* modulep;
AstNodeClassDType* classp;
AstNodeDType* dtypep;

View File

@ -150,6 +150,14 @@ private:
m_scopep->addActivep(clonep);
clonep->iterateChildren(*this); // We iterate under the *clone*
}
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
// Copy under the scope but don't recurse
UINFO(4," Move "<<nodep<<endl);
AstNode* clonep = nodep->cloneTree(false);
nodep->user2p(clonep);
m_scopep->addActivep(clonep);
clonep->iterateChildren(*this); // We iterate under the *clone*
}
virtual void visit(AstAssignW* nodep, AstNUser*) {
// Add to list of blocks under this scope
UINFO(4," Move "<<nodep<<endl);
@ -215,12 +223,17 @@ private:
virtual void visit(AstVarRef* nodep, AstNUser*) {
// VarRef needs to point to VarScope
// Make sure variable has made user1p.
nodep->varp()->accept(*this);
AstVarScope* varscp = nodep->packagep()
? (AstVarScope*)nodep->varp()->user3p()
: (AstVarScope*)nodep->varp()->user1p();
if (!varscp) nodep->v3fatalSrc("Can't locate varref scope");
nodep->varScopep(varscp);
if (!nodep->varp()) nodep->v3fatalSrc("Unlinked");
if (nodep->varp()->isIfaceRef()) {
nodep->varScopep(NULL);
} else {
nodep->varp()->accept(*this);
AstVarScope* varscp = nodep->packagep()
? (AstVarScope*)nodep->varp()->user3p()
: (AstVarScope*)nodep->varp()->user1p();
if (!varscp) nodep->v3fatalSrc("Can't locate varref scope");
nodep->varScopep(varscp);
}
}
virtual void visit(AstScopeName* nodep, AstNUser*) {
// If there's a %m in the display text, we add a special node that will contain the name()
@ -298,6 +311,9 @@ private:
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
movedDeleteOrIterate(nodep);
}
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
movedDeleteOrIterate(nodep);
}
virtual void visit(AstAssignW* nodep, AstNUser*) {
movedDeleteOrIterate(nodep);
}

View File

@ -183,6 +183,18 @@ public:
}
return any;
}
void importFromIface(VSymGraph* graphp, const VSymEnt* srcp) {
// Import interface tokens from source symbol table into this symbol table, recursively
UINFO(9, " importIf se"<<(void*)this<<" from se"<<(void*)srcp<<endl);
for (IdNameMap::const_iterator it=srcp->m_idNameMap.begin(); it!=srcp->m_idNameMap.end(); ++it) {
const string& name = it->first;
VSymEnt* srcp = it->second;
VSymEnt* symp = new VSymEnt(graphp, srcp);
reinsert(name, symp);
// And recurse to create children
srcp->importFromIface(graphp, symp);
}
}
void cellErrorScopes(AstNode* lookp, string prettyName="") {
if (prettyName=="") prettyName = lookp->prettyName();
string scopes;

View File

@ -1073,6 +1073,14 @@ private:
nodep->dtypeFrom(nodep->lhsp());
}
virtual void visit(AstIfaceRefDType* nodep, AstNUser* vup) {
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
UINFO(5," IFACEREF "<<nodep<<endl);
nodep->iterateChildren(*this, vup);
nodep->dtypep(nodep);
nodep->widthForce(1, 1); // Not really relevant
UINFO(4,"dtWidthed "<<nodep<<endl);
}
virtual void visit(AstNodeClassDType* nodep, AstNUser* vup) {
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
UINFO(5," NODECLASS "<<nodep<<endl);

View File

@ -417,6 +417,7 @@ word [a-zA-Z0-9_]+
"do" { FL; return yDO; }
"endclocking" { FL; return yENDCLOCKING; }
"endpackage" { FL; return yENDPACKAGE; }
"endinterface" { FL; return yENDINTERFACE; }
"endprogram" { FL; return yENDPROGRAM; }
"endproperty" { FL; return yENDPROPERTY; }
"enum" { FL; return yENUM; }
@ -425,9 +426,11 @@ word [a-zA-Z0-9_]+
"iff" { FL; return yIFF; }
"import" { FL; return yIMPORT; }
"inside" { FL; return yINSIDE; }
"interface" { FL; return yINTERFACE; }
"int" { FL; return yINT; }
"logic" { FL; return yLOGIC; }
"longint" { FL; return yLONGINT; }
"modport" { FL; return yMODPORT; }
"package" { FL; return yPACKAGE; }
"packed" { FL; return yPACKED; }
"priority" { FL; return yPRIORITY; }
@ -460,7 +463,6 @@ word [a-zA-Z0-9_]+
"dist" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"endclass" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"endgroup" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"endinterface" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"endsequence" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"expect" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"extends" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
@ -470,13 +472,11 @@ word [a-zA-Z0-9_]+
"forkjoin" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"ignore_bins" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"illegal_bins" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"interface" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"intersect" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"join_any" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"join_none" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"local" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"matches" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"modport" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"new" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"null" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
"protected" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }

View File

@ -310,6 +310,7 @@ class AstSenTree;
%token<fl> yENDCLOCKING "endclocking"
%token<fl> yENDFUNCTION "endfunction"
%token<fl> yENDGENERATE "endgenerate"
%token<fl> yENDINTERFACE "endinterface"
%token<fl> yENDMODULE "endmodule"
%token<fl> yENDPACKAGE "endpackage"
%token<fl> yENDPRIMITIVE "endprimitive"
@ -337,9 +338,11 @@ class AstSenTree;
%token<fl> yINSIDE "inside"
%token<fl> yINT "int"
%token<fl> yINTEGER "integer"
%token<fl> yINTERFACE "interface"
%token<fl> yLOCALPARAM "localparam"
%token<fl> yLOGIC "logic"
%token<fl> yLONGINT "longint"
%token<fl> yMODPORT "modport"
%token<fl> yMODULE "module"
%token<fl> yNAND "nand"
%token<fl> yNEGEDGE "negedge"
@ -650,7 +653,7 @@ descriptionList: // IEEE: part of source_text
description: // ==IEEE: description
module_declaration { }
// // udp_declaration moved into module_declaration
//UNSUP interface_declaration { }
| interface_declaration { }
| program_declaration { }
| package_declaration { }
| package_item { if ($1) GRAMMARP->unitPackage($1->fileline())->addStmtp($1); }
@ -844,14 +847,24 @@ port<nodep>: // ==IEEE: port
// // IEEE: interface_port_header port_identifier { unpacked_dimension }
// // Expanded interface_port_header
// // We use instantCb here because the non-port form looks just like a module instantiation
//UNSUP portDirNetE id/*interface*/ idAny/*port*/ rangeListE sigAttrListE
//UNSUP { VARDTYPE($2); VARDONEA($<fl>3, $3, $4); PARSEP->instantCb($<fl>2, $2, $3, $4); PINNUMINC(); }
//UNSUP portDirNetE yINTERFACE idAny/*port*/ rangeListE sigAttrListE
//UNSUP { VARDTYPE($2); VARDONEA($<fl>3, $3, $4); PINNUMINC(); }
//UNSUP portDirNetE id/*interface*/ '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE
//UNSUP { VARDTYPE($2); VARDONEA($<fl>5, $5, $6); PARSEP->instantCb($<fl>2, $2, $5, $6); PINNUMINC(); }
//UNSUP portDirNetE yINTERFACE '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE
//UNSUP { VARDTYPE($2); VARDONEA($<fl>5, $5, $6); PINNUMINC(); }
portDirNetE id/*interface*/ idAny/*port*/ variable_dimensionListE sigAttrListE
{ $$ = new AstPort($<fl>2,PINNUMINC(),*$3);
AstVar* varp=new AstVar($<fl>2,AstVarType(AstVarType::IFACEREF),*$3,VFlagChildDType(),
new AstIfaceRefDType($<fl>2,"",*$2));
if ($4) varp->v3error("Unsupported: Arrayed interfaces");
varp->addAttrsp($5);
$$->addNext(varp); }
| portDirNetE yINTERFACE idAny/*port*/ rangeListE sigAttrListE
{ $<fl>2->v3error("Unsupported: virtual interfaces"); }
| portDirNetE id/*interface*/ '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE
{ $$ = new AstPort($3,PINNUMINC(),*$5);
AstVar* varp=new AstVar($<fl>2,AstVarType(AstVarType::IFACEREF),*$5,VFlagChildDType(),
new AstIfaceRefDType($<fl>2,"",*$2,*$4));
if ($6) varp->v3error("Unsupported: Arrayed interfaces");
varp->addAttrsp($7);
$$->addNext(varp); }
| portDirNetE yINTERFACE '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE
{ $<fl>2->v3error("Unsupported: virtual interfaces"); }
//
// // IEEE: ansi_port_declaration, with [port_direction] removed
// // IEEE: [ net_port_header | interface_port_header ] port_identifier { unpacked_dimension } [ '=' constant_expression ]
@ -889,7 +902,7 @@ port<nodep>: // ==IEEE: port
//UNSUP portDirNetE /*implicit*/ '.' portSig '(' portAssignExprE ')' sigAttrListE
//UNSUP { UNSUP }
//
portDirNetE data_type portSig variable_dimensionListE sigAttrListE
| portDirNetE data_type portSig variable_dimensionListE sigAttrListE
{ $$=$3; VARDTYPE($2); $$->addNextNull(VARDONEP($$,$4,$5)); }
| portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE
{ $$=$4; VARDTYPE($3); $$->addNextNull(VARDONEP($$,$5,$6)); }
@ -932,6 +945,54 @@ portSig<nodep>:
//**********************************************************************
// Interface headers
interface_declaration: // IEEE: interface_declaration + interface_nonansi_header + interface_ansi_header:
// // timeunits_delcarationE is instead in interface_item
intFront parameter_port_listE portsStarE ';'
interface_itemListE yENDINTERFACE endLabelE
{ if ($2) $1->addStmtp($2);
if ($3) $1->addStmtp($3);
if ($5) $1->addStmtp($5);
SYMP->popScope($1); }
//UNSUP yEXTERN intFront parameter_port_listE portsStarE ';' { }
;
intFront<modulep>:
yINTERFACE lifetimeE idAny/*new_interface*/
{ $$ = new AstIface($1,*$3);
$$->inLibrary(true);
PARSEP->rootp()->addModulep($$);
SYMP->pushNew($$); }
;
interface_itemListE<nodep>:
/* empty */ { $$ = NULL; }
| interface_itemList { $$ = $1; }
;
interface_itemList<nodep>:
interface_item { $$ = $1; }
| interface_itemList interface_item { $$ = $1->addNextNull($2); }
;
interface_item<nodep>: // IEEE: interface_item + non_port_interface_item
port_declaration ';' { $$ = $1; }
// // IEEE: non_port_interface_item
//UNSUP generate_region { $$ = $1; }
| interface_or_generate_item { $$ = $1; }
//UNSUP program_declaration { $$ = $1; }
//UNSUP interface_declaration { $$ = $1; }
| timeunits_declaration { $$ = $1; }
// // See note in interface_or_generate item
| module_common_item { $$ = $1; }
;
interface_or_generate_item<nodep>: // ==IEEE: interface_or_generate_item
// // module_common_item in interface_item, as otherwise duplicated
// // with module_or_generate_item's module_common_item
modport_declaration { $$ = $1; }
//UNSUP extern_tf_declaration { $$ = $1; }
;
//**********************************************************************
// Program headers
@ -989,6 +1050,46 @@ program_generate_item<nodep>: // ==IEEE: program_generate_item
//UNSUP elaboration_system_task { $$ = $1; }
;
modport_declaration<nodep>: // ==IEEE: modport_declaration
yMODPORT modport_itemList ';' { $$ = $2; }
;
modport_itemList<nodep>: // IEEE: part of modport_declaration
modport_item { $$ = $1; }
| modport_itemList ',' modport_item { $$ = $1->addNextNull($3); }
;
modport_item<nodep>: // ==IEEE: modport_item
id/*new-modport*/ '(' modportPortsDeclList ')' { $$ = new AstModport($2,*$1,$3); }
;
modportPortsDeclList<modportvarrefp>:
modportPortsDecl { $$ = $1; }
| modportPortsDeclList ',' modportPortsDecl { $$ = $1->addNextNull($3)->castModportVarRef(); }
;
// IEEE: modport_ports_declaration + modport_simple_ports_declaration
// + (modport_tf_ports_declaration+import_export) + modport_clocking_declaration
// We've expanded the lists each take to instead just have standalone ID ports.
// We track the type as with the V2k series of defines, then create as each ID is seen.
modportPortsDecl<modportvarrefp>:
// // IEEE: modport_simple_ports_declaration
port_direction modportSimplePort { $$ = new AstModportVarRef($<fl>1,*$2,GRAMMARP->m_varIO); }
// // IEEE: modport_clocking_declaration
//UNSUP yCLOCKING idAny/*clocking_identifier*/ { }
//UNSUP yIMPORT modport_tf_port { }
//UNSUP yEXPORT modport_tf_port { }
// Continuations of above after a comma.
// // IEEE: modport_simple_ports_declaration
| modportSimplePort { $$ = new AstModportVarRef($<fl>1,*$1,AstVarType::INOUT); }
;
modportSimplePort<strp>: // IEEE: modport_simple_port or modport_tf_port, depending what keyword was earlier
id { $$ = $1; }
//UNSUP '.' idAny '(' ')' { }
//UNSUP '.' idAny '(' expr ')' { }
;
//************************************************
// Variable Declarations

View File

@ -7,8 +7,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug102");
compile (
);

View File

@ -100,7 +100,7 @@ interface handshake #(
// local logic (counter)
always @ (posedge clk, posedge rst)
if (rst) cnt <= '0;
else cnt <= cnt + inc;
else cnt <= cnt + {31'h0, inc};
endinterface : handshake
@ -129,7 +129,7 @@ module source #(
// counter
always @ (posedge clk, posedge rst)
if (rst) cnt <= 32'd0;
else cnt <= cnt + (inf.req & inf.grt);
else cnt <= cnt + {31'd0, (inf.req & inf.grt)};
// request signal
assign inf.req = rnd[0];
@ -161,7 +161,7 @@ module drain #(
// counter
always @ (posedge clk, posedge rst)
if (rst) cnt <= 32'd0;
else cnt <= cnt + (inf.req & inf.grt);
else cnt <= cnt + {31'd0, (inf.req & inf.grt)};
// grant signal
assign inf.grt = rnd[0];

View File

@ -7,10 +7,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug102");
compile (
verilator_flags2 => ["--lint-only"]
);
execute (
check_finished=>1,
);
ok(1);

View File

@ -0,0 +1,45 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2013 by Wilson Snyder.
// Very simple test for interface pathclearing
interface ifc;
logic [3:0] value;
endinterface
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=1;
ifc itop();
sub c1 (.isub(itop),
.i_value(4'h4));
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc==20) begin
if (c1.i_value != 4) $stop; // 'Normal' crossref just for comparison
if (itop.value != 4) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module sub
(
ifc isub,
input logic [3:0] i_value
);
always @* begin
isub.value = i_value;
end
endmodule : sub

View File

@ -0,0 +1,18 @@
#!/usr/bin/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.
compile (
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,48 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2013 by Wilson Snyder.
// Very simple test for interface pathclearing
interface ifc;
integer hidden_from_isub;
integer value;
modport out_modport (output value);
endinterface
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=1;
ifc itop();
sub c1 (.isub(itop),
.i_value(4));
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc==20) begin
if (itop.value != 4) $stop;
itop.hidden_from_isub = 20;
if (itop.hidden_from_isub != 20) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module sub
(
ifc.out_modport isub,
input integer i_value
);
always @* begin
isub.value = i_value;
end
endmodule

View File

@ -7,10 +7,8 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug102");
compile (
v_flags => []
verilator_flags2 => ["--top-module t"],
);
execute (

View File

@ -3,11 +3,6 @@
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2010 by Wilson Snyder.
interface counter_io;
logic [3:0] value;
logic reset;
endinterface
module t (/*AUTOARG*/
// Inputs
clk
@ -18,17 +13,24 @@ module t (/*AUTOARG*/
counter_io c1_data();
counter_io c2_data();
//counter_io c3_data; // IEEE illegal, and VCS doesn't allow non-() as it does with cells
counter_io c3_data();
counter c1 (.clkm(clk),
.c_data(c1_data),
.i_value(4'h1));
counter2 c2 (.clkm(clk),
.c_data(c2_data),
.i_value(4'h2));
counter_ansi c1 (.clkm(clk),
.c_data(c1_data),
.i_value(4'h1));
counter_ansi c2 (.clkm(clk),
.c_data(c2_data),
.i_value(4'h2));
`ifdef VERILATOR counter_ansi `else counter_nansi `endif
/**/ c3 (.clkm(clk),
.c_data(c3_data),
.i_value(4'h3));
initial begin
c1_data.value = 4'h4;
c2_data.value = 4'h5;
c3_data.value = 4'h6;
end
always @ (posedge clk) begin
@ -36,46 +38,71 @@ module t (/*AUTOARG*/
if (cyc<2) begin
c1_data.reset <= 1;
c2_data.reset <= 1;
c3_data.reset <= 1;
end
if (cyc==2) begin
c1_data.reset <= 0;
c2_data.reset <= 0;
c3_data.reset <= 0;
end
if (cyc==3) begin
if (c1_data.get_lcl() != 12345) $stop;
end
if (cyc==20) begin
$write("[%0t] c1 cyc%0d: %0x %0x\n", $time, cyc, c1_data.value, c1_data.reset);
$write("[%0t] c2 cyc%0d: %0x %0x\n", $time, cyc, c2_data.value, c2_data.reset);
$write("[%0t] c1 cyc%0d: c1 %0x %0x c2 %0x %0x c3 %0x %0x\n", $time, cyc,
c1_data.value, c1_data.reset,
c2_data.value, c2_data.reset,
c3_data.value, c3_data.reset);
if (c1_data.value != 2) $stop;
if (c2_data.value != 3) $stop;
if (c3_data.value != 4) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module counter
interface counter_io;
logic [3:0] value;
logic reset;
integer lcl;
task set_lcl (input integer a); lcl=a; endtask
function integer get_lcl (); return lcl; endfunction
endinterface
interface ifunused;
logic unused;
endinterface
module counter_ansi
(
input clkm,
counter_io c_data,
input logic [3:0] i_value
);
always @ (posedge clkm) begin
if (c_data.reset)
c_data.value <= i_value;
else
c_data.value <= c_data.value + 1;
initial begin
c_data.set_lcl(12345);
end
endmodule : counter
module counter2(clkm, c_data, i_value);
always @ (posedge clkm) begin
c_data.value <= c_data.reset ? i_value : c_data.value + 1;
end
endmodule : counter_ansi
`ifndef VERILATOR
// non-ansi modports not seen in the wild yet. Verilog-Perl needs parser improvement too.
module counter_nansi(clkm, c_data, i_value);
input clkm;
counter_io c_data;
input logic [3:0] i_value;
always @ (posedge clkm) begin
if (c_data.reset)
c_data.value <= i_value;
else
c_data.value <= c_data.value + 1;
c_data.value <= c_data.reset ? i_value : c_data.value + 1;
end
endmodule : counter2
endmodule : counter_nansi
`endif
module modunused (ifunused ifinunused);
ifunused ifunused();
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/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.
compile (
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,72 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2013 by Wilson Snyder.
interface ifc;
integer value;
endinterface
module t (/*AUTOARG*/
// Inputs
clk
);
`ifdef INLINE_A //verilator inline_module
`else //verilator no_inline_module
`endif
input clk;
integer cyc=1;
ifc itop1a();
ifc itop1b();
ifc itop2a();
ifc itop2b();
wrapper c1 (.isuba(itop1a),
.isubb(itop1b),
.i_valuea(14),
.i_valueb(15));
wrapper c2 (.isuba(itop2a),
.isubb(itop2b),
.i_valuea(24),
.i_valueb(25));
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc==20) begin
if (itop1a.value != 14) $stop;
if (itop1b.value != 15) $stop;
if (itop2a.value != 24) $stop;
if (itop2b.value != 25) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module wrapper
(
ifc isuba,
ifc isubb,
input integer i_valuea,
input integer i_valueb
);
`ifdef INLINE_B //verilator inline_module
`else //verilator no_inline_module
`endif
lower subsuba (.isub(isuba), .i_value(i_valuea));
lower subsubb (.isub(isubb), .i_value(i_valueb));
endmodule
module lower
(
ifc isub,
input integer i_value
);
`ifdef INLINE_C //verilator inline_module
`else //verilator no_inline_module
`endif
always @* begin
isub.value = i_value;
end
endmodule

View File

@ -0,0 +1,21 @@
#!/usr/bin/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.
$Self->{vlt} and $Self->unsupported("Verilator unsupported, interface generates changing types");
$Self->{vcs} and $Self->unsupported("Commercially unsupported, interface crossrefs");
compile (
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,78 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2013 by Wilson Snyder.
// This test demonstrates how not only parameters but the type of a parent
// interface could propagate down to child modules, changing their data type
// determinations. Note presently unsupported in all commercial simulators.
interface ifc;
parameter MODE = 0;
generate
// Note block must be named per SystemVerilog 2005
if (MODE==1) begin : g
integer value;
end
else if (MODE==2) begin : g
real value;
end
endgenerate
endinterface
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=1;
ifc #(1) itop1a();
ifc #(1) itop1b();
ifc #(2) itop2a();
ifc #(2) itop2b();
wrapper c1 (.isuba(itop1a),
.isubb(itop1b),
.i_valuea(14.1),
.i_valueb(15.2));
wrapper c2 (.isuba(itop2a),
.isubb(itop2b),
.i_valuea(24.3),
.i_valueb(25.4));
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc==20) begin
if (itop1a.g.value != 14) $stop;
if (itop1b.g.value != 15) $stop;
if (itop2a.g.value != 24) $stop;
if (itop2b.g.value != 25) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module wrapper
(
ifc isuba,
ifc isubb,
input real i_valuea,
input real i_valueb
);
lower subsuba (.isub(isuba), .i_value(i_valuea));
lower subsubb (.isub(isubb), .i_value(i_valueb));
endmodule
module lower
(
ifc isub,
input real i_value
);
always @* begin
`error Commercial sims choke on cross ref here
isub.g.value = i_value;
end
endmodule

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_interface_down.v");
compile (
v_flags2 => ['+define+INLINE_A'],
verilator_flags2 => ['-trace'],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_interface_down.v");
compile (
v_flags2 => ['+define+INLINE_A +define+INLINE_B'],
verilator_flags2 => ['-trace'],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_interface_down.v");
compile (
v_flags2 => ['+define+INLINE_A +define+INLINE_C'],
verilator_flags2 => ['-trace'],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_interface_down.v");
compile (
v_flags2 => ['+define+INLINE_A +define+INLINE_D'],
verilator_flags2 => ['-trace'],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_interface_down.v");
compile (
v_flags2 => ['+define+INLINE_B'],
verilator_flags2 => ['-trace'],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_interface_down.v");
compile (
v_flags2 => ['+define+INLINE_B +define+INLINE_C'],
verilator_flags2 => ['-trace'],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_interface_down.v");
compile (
v_flags2 => ['+define+INLINE_B +define+INLINE_D'],
verilator_flags2 => ['-trace'],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_interface_down.v");
compile (
v_flags2 => ['+define+INLINE_C'],
verilator_flags2 => ['-trace'],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_interface_down.v");
compile (
v_flags2 => ['+define+INLINE_C +define+INLINE_D'],
verilator_flags2 => ['-trace'],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
top_filename("t/t_interface_down.v");
compile (
v_flags2 => ['+define+INLINE_D'],
verilator_flags2 => ['-trace'],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,18 @@
#!/usr/bin/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.
compile (
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,87 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2013 by Wilson Snyder.
// Very simple test for interface pathclearing
`ifdef VCS
`define UNSUPPORTED_MOD_IN_GENS
`endif
`ifdef VERILATOR
`define UNSUPPORTED_MOD_IN_GENS
`endif
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=1;
ifc #(1) itopa();
ifc #(2) itopb();
sub #(1) ca (.isub(itopa),
.i_value(4));
sub #(2) cb (.isub(itopb),
.i_value(5));
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc==1) begin
if (itopa.MODE != 1) $stop;
if (itopb.MODE != 2) $stop;
end
if (cyc==20) begin
if (itopa.get_value() != 4) $stop;
if (itopb.get_value() != 5) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module sub
#(parameter MODE = 0)
(
ifc.out_modport isub,
input integer i_value
);
`ifdef UNSUPPORTED_MOD_IN_GENS
always @* isub.value = i_value;
`else
generate if (MODE == 1) begin
always @* isub.valuea = i_value;
end
else if (MODE == 2) begin
always @* isub.valueb = i_value;
end
endgenerate
`endif
endmodule
interface ifc;
parameter MODE = 0;
// Modports under generates not supported by all commercial simulators
`ifdef UNSUPPORTED_MOD_IN_GENS
integer value;
modport out_modport (output value);
function integer get_value(); return value; endfunction
`else
generate if (MODE == 0) begin
integer valuea;
modport out_modport (output valuea);
function integer get_valuea(); return valuea; endfunction
end
else begin
integer valueb;
modport out_modport (output valueb);
function integer get_valueb(); return valueb; endfunction
end
endgenerate
`endif
endinterface

View File

@ -0,0 +1,18 @@
#!/usr/bin/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.
compile (
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,70 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2013 by Wilson Snyder.
// Very simple test for interface pathclearing
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=1;
ifc #(2) itopa();
ifc #(4) itopb();
sub ca (.isub(itopa),
.clk);
sub cb (.isub(itopb),
.clk);
always @ (posedge clk) begin
`ifdef TEST_VERBOSE
$write("[%0t] cyc==%0d result=%b %b\n",$time, cyc, itopa.valueo, itopb.valueo);
`endif
cyc <= cyc + 1;
itopa.valuei <= cyc[1:0];
itopb.valuei <= cyc[3:0];
if (cyc==1) begin
if (itopa.WIDTH != 2) $stop;
if (itopb.WIDTH != 4) $stop;
if ($bits(itopa.valueo) != 2) $stop;
if ($bits(itopb.valueo) != 4) $stop;
if ($bits(itopa.out_modport.valueo) != 2) $stop;
if ($bits(itopb.out_modport.valueo) != 4) $stop;
end
if (cyc==4) begin
if (itopa.valueo != 2'b11) $stop;
if (itopb.valueo != 4'b0011) $stop;
end
if (cyc==5) begin
if (itopa.valueo != 2'b00) $stop;
if (itopb.valueo != 4'b0100) $stop;
end
if (cyc==20) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
interface ifc
#(parameter WIDTH = 1);
// verilator lint_off MULTIDRIVEN
logic [WIDTH-1:0] valuei;
logic [WIDTH-1:0] valueo;
// verilator lint_on MULTIDRIVEN
modport out_modport (input valuei, output valueo);
endinterface
// Note not parameterized
module sub
(
ifc.out_modport isub,
input clk
);
always @(posedge clk) isub.valueo <= isub.valuei + 1;
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/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.
compile (
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,70 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2013 by Wilson Snyder.
// Very simple test for interface pathclearing
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=1;
ifc #(2) itopa();
ifc #(4) itopb();
sub ca (.isub(itopa.out_modport),
.clk);
sub cb (.isub(itopb.out_modport),
.clk);
always @ (posedge clk) begin
`ifdef TEST_VERBOSE
$write("[%0t] cyc==%0d result=%b %b\n",$time, cyc, itopa.valueo, itopb.valueo);
`endif
cyc <= cyc + 1;
itopa.valuei <= cyc[1:0];
itopb.valuei <= cyc[3:0];
if (cyc==1) begin
if (itopa.WIDTH != 2) $stop;
if (itopb.WIDTH != 4) $stop;
if ($bits(itopa.valueo) != 2) $stop;
if ($bits(itopb.valueo) != 4) $stop;
if ($bits(itopa.out_modport.valueo) != 2) $stop;
if ($bits(itopb.out_modport.valueo) != 4) $stop;
end
if (cyc==4) begin
if (itopa.valueo != 2'b11) $stop;
if (itopb.valueo != 4'b0011) $stop;
end
if (cyc==5) begin
if (itopa.valueo != 2'b00) $stop;
if (itopb.valueo != 4'b0100) $stop;
end
if (cyc==20) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
interface ifc
#(parameter WIDTH = 1);
// verilator lint_off MULTIDRIVEN
logic [WIDTH-1:0] valuei;
logic [WIDTH-1:0] valueo;
// verilator lint_on MULTIDRIVEN
modport out_modport (input valuei, output valueo);
endinterface
// Note not parameterized
module sub
(
ifc.out_modport isub,
input clk
);
always @(posedge clk) isub.valueo <= isub.valuei + 1;
endmodule

View File

@ -7,13 +7,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug102");
top_filename("t/t_interface.v");
compile (
# Avoid inlining so we find bugs in the non-inliner connection code
v_flags => ["-Oi"],
verilator_flags2 => ["-Oi"],
);
execute (

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
compile (
verilator_flags2 => ["--lint-only"],
verilator_make_gcc => 0,
make_top_shell => 0,
make_main => 0,
fails => 1,
expect=>
'%Error: t/t_interface_mismodport_bad.v:\d+: Can\'t find definition of \'bad\' in dotted signal: isub.bad
.*%Error: Exiting due to.*',
);
ok(1);
1;

View File

@ -0,0 +1,37 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2013 by Wilson Snyder.
interface ifc;
integer ok;
integer bad;
modport out_modport (output ok);
endinterface
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=1;
ifc itop();
counter_ansi c1 (.isub(itop),
.i_value(4'h4));
endmodule
module counter_ansi
(
ifc.out_modport isub,
input logic [3:0] i_value
);
always @* begin
isub.ok = i_value;
isub.bad = i_value; // Illegal access
end
endmodule

View File

@ -7,10 +7,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug102");
compile (
v_flags => [],
);
execute (

View File

@ -3,13 +3,26 @@
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2010 by Wilson Snyder.
interface counter_io;
interface counter_if;
logic [3:0] value;
logic reset;
modport counter_side (input reset, output value);
modport core_side (output reset, input value);
modport counter_mp (input reset, output value);
modport core_mp (output reset, input value);
endinterface
// Check can have inst module before top module
module counter_ansi
(
input clkm,
counter_if c_data,
input logic [3:0] i_value
);
always @ (posedge clkm) begin
c_data.value <= c_data.reset ? i_value : c_data.value + 1;
end
endmodule : counter_ansi
module t (/*AUTOARG*/
// Inputs
clk
@ -18,19 +31,31 @@ module t (/*AUTOARG*/
input clk;
integer cyc=1;
counter_io c1_data();
counter_io c2_data();
counter_if c1_data();
counter_if c2_data();
counter_if c3_data();
counter_if c4_data();
counter c1 (.clkm(clk),
.c_data(c1_data),
.i_value(4'h1));
counter c2 (.clkm(clk),
.c_data(c2_data.counter_side),
.i_value(4'h2));
counter_ansi c1 (.clkm(clk),
.c_data(c1_data.counter_mp),
.i_value(4'h1));
`ifdef VERILATOR counter_ansi `else counter_nansi `endif
/**/ c2 (.clkm(clk),
.c_data(c2_data.counter_mp),
.i_value(4'h2));
counter_ansi_m c3 (.clkm(clk),
.c_data(c3_data),
.i_value(4'h3));
`ifdef VERILATOR counter_ansi_m `else counter_nansi_m `endif
/**/ c4 (.clkm(clk),
.c_data(c4_data),
.i_value(4'h4));
initial begin
c1_data.value = 4'h4;
c2_data.value = 4'h5;
c3_data.value = 4'h6;
c4_data.value = 4'h7;
end
always @ (posedge clk) begin
@ -38,33 +63,69 @@ module t (/*AUTOARG*/
if (cyc<2) begin
c1_data.reset <= 1;
c2_data.reset <= 1;
c3_data.reset <= 1;
c4_data.reset <= 1;
end
if (cyc==2) begin
c1_data.reset <= 0;
c2_data.reset <= 0;
c3_data.reset <= 0;
c4_data.reset <= 0;
end
if (cyc==20) begin
$write("[%0t] c1 cyc%0d: %0x %0x\n", $time, cyc, c1_data.value, c1_data.reset);
$write("[%0t] c2 cyc%0d: %0x %0x\n", $time, cyc, c2_data.value, c2_data.reset);
$write("[%0t] cyc%0d: c1 %0x %0x c2 %0x %0x c3 %0x %0x c4 %0x %0x\n", $time, cyc,
c1_data.value, c1_data.reset,
c2_data.value, c2_data.reset,
c3_data.value, c3_data.reset,
c4_data.value, c4_data.reset);
if (c1_data.value != 2) $stop;
if (c2_data.value != 3) $stop;
if (c3_data.value != 4) $stop;
if (c4_data.value != 5) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module counter
`ifndef VERILATOR
// non-ansi modports not seen in the wild yet. Verilog-Perl needs parser improvement too.
module counter_nansi
(clkm, c_data, i_value);
input clkm;
counter_if c_data;
input logic [3:0] i_value;
always @ (posedge clkm) begin
c_data.value <= c_data.reset ? i_value : c_data.value + 1;
end
endmodule : counter_nansi
`endif
module counter_ansi_m
(
input clkm,
counter_io c_data,
counter_if.counter_mp c_data,
input logic [3:0] i_value
);
always @ (posedge clkm) begin
if (c_data.reset)
c_data.value <= i_value;
else
c_data.value <= c_data.value + 1;
c_data.value <= c_data.reset ? i_value : c_data.value + 1;
end
endmodule : counter
endmodule : counter_ansi_m
`ifndef VERILATOR
// non-ansi modports not seen in the wild yet. Verilog-Perl needs parser improvement too.
module counter_nansi_m
(clkm, c_data, i_value);
input clkm;
counter_if.counter_mp c_data;
input logic [3:0] i_value;
always @ (posedge clkm) begin
c_data.value <= c_data.reset ? i_value : c_data.value + 1;
end
endmodule : counter_nansi_m
`endif

View File

@ -7,13 +7,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug102");
top_filename("t/t_interface_modport.v");
compile (
# Avoid inlining so we find bugs in the non-inliner connection code
v_flags => ["-Oi"],
verilator_flags2 => ["-Oi"],
);
execute (

View File

@ -0,0 +1,21 @@
#!/usr/bin/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.
compile (
verilator_flags2 => ["--lint-only"],
verilator_make_gcc => 0,
make_top_shell => 0,
make_main => 0,
fails => 1,
expect=>
'%Error: t/t_interface_top_bad.v:\d+: Unsupported: Interfaced port on top level module',
);
ok(1);
1;

View File

@ -3,17 +3,17 @@
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2010 by Wilson Snyder.
interface counter_io;
interface ifc;
logic [3:0] value;
logic reset;
modport counter_side (input reset, output value);
modport core_side (output reset, input value);
modport counter_mp (input reset, output value);
modport core_mp (output reset, input value);
endinterface
module t
(// Inputs
input clk,
counter_io.counter_side c_data
ifc.counter_mp c_data
);
integer cyc=1;

View File

@ -10,7 +10,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
$Self->{vlt} or $Self->skip("Verilator only test");
compile (
v_flags => ["-Wno-IMPLICIT"],
v_flags2 => ["-Wno-IMPLICIT"],
);
ok(1);

View File

@ -8,7 +8,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
compile (
v_flags => [],
);
execute (