diff --git a/Changes b/Changes index beda0e6c8..244f99fb6 100644 --- a/Changes +++ b/Changes @@ -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] diff --git a/bin/verilator b/bin/verilator index 2616245b6..4784bacb4 100755 --- a/bin/verilator +++ b/bin/verilator @@ -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 diff --git a/src/V3Ast.h b/src/V3Ast.h index 7a0c4048f..0413cc191 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -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 diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 979f57b50..178d8ffba 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -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="< "; 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<<" "< "; varp()->dump(str); } + else { str<<" -> UNLINKED"; } +} void AstPin::dump(ostream& str) { this->AstNode::dump(str); if (modVarp()) { str<<" -> "; modVarp()->dump(str); } diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index e140ee05d..8d6e1c9a5 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -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; diff --git a/src/V3Const.cpp b/src/V3Const.cpp index acf55b68c..e69149dd8 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -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; diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 581d71bfa..99ce87a7f 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -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()); diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index ecdbe2a4c..c0a7b6fef 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -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 diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index ad8a9dfc3..127cc1965 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -190,6 +190,7 @@ private: <<"' does not match "<typeName()<<" name: "<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: "<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: "<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 . + // 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); } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 2917ea472..c98be5420 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -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 NameScopeSymMap; - typedef set > ImplicitNameSet; + typedef multimap NameScopeSymMap; + typedef map ScopeAliasMap; + typedef set > ImplicitNameSet; + typedef vector IfaceVarSyms; + typedef vector > 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 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 "<first<<" -> "<second<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<<" "<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(modportp); + ifOrPortSymp = foundp; + ok = true; + } + } + if (!ok) ifacerefp->v3error("Modport not found under interface '" + <prettyName(ifacerefp->ifaceName()) + <<"': "<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<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"<nodep()<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: "<prettyName()); - } else if (!refp->isIO()) { - nodep->v3error("Pin is not an in/out/inout: "<prettyName()); + } else if (!refp->isIO() && !refp->isIfaceRef()) { + nodep->v3error("Pin is not an in/out/inout/interface: "<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 "<varp()->name()<<" "<varp()->dtypep()->castIfaceRefDType(); + if (!dtypep) nodep->v3fatalSrc("Non AstIfaceRefDType on isIfaceRef() var"); + UINFO(9,"Iface parent dtype "<cellName(); + string baddot; VSymEnt* okSymp; + VSymEnt* cellSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp); + if (!cellSymp) nodep->v3fatalSrc("No symbol for interface cell: " <prettyName(ifcellname)); + UINFO(5, " Found interface cell: se"<<(void*)cellSymp<<" "<nodep()<modportName()!="") { + VSymEnt* mpSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp); + if (!mpSymp) { nodep->v3fatalSrc("No symbol for interface modport: " <prettyName(dtypep->modportName())); } + else cellSymp = mpSymp; + UINFO(5, " Found modport cell: se"<<(void*)cellSymp<<" "<nodep()<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 "<=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: "<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: "<nodep()<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__<<": "<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: "<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: "<iterateChildren(*this); + VSymEnt* symp = m_curSymp->findIdFallback(nodep->name()); + if (!symp) { + nodep->v3error("Modport item not found: "<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: "<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__<<": "<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: "<prettyName()); - } else if (!refp->isIO() && !refp->isParam()) { - nodep->v3error("Pin is not an in/out/inout/param: "<prettyName()); + } else if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) { + nodep->v3error("Pin is not an in/out/inout/param/interface: "<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: "<name(); + m_ds.m_dotSymp = foundp; + m_ds.m_dotPos = DP_SCOPE; + UINFO(9," cell -> iface varref "<nodep()<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 "<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 "<nodep()<nodep()->castCell() + || !m_ds.m_dotSymp->nodep()->castCell()->modp() + || !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) { + nodep->v3error("Modport not referenced as ."<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: "<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); } diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 6cdae56eb..16c3ad025 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -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*) { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 7e962d908..52ce09aff 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -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 > IfaceRefRefs; // Note may have duplicate entries + // STATE - typedef std::map VarCloneMap; + typedef map 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 ModNameMap; + typedef map ModNameMap; ModNameMap m_modNameMap; // Hash of created module flavors by name - typedef std::map LongMap; + typedef map LongMap; LongMap m_longMap; // Hash of very long names to unique identity number int m_longId; + typedef map ValueMap; + typedef map 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 LevelModMap; LevelModMap m_todoModps; // Modules left to process + typedef deque 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(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 "<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? "<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: "<=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 "<exprp() + || !pinp->exprp()->castVarRef() + || !pinp->exprp()->castVarRef()->varp() + || !pinp->exprp()->castVarRef()->varp()->subDTypep() + || !pinp->exprp()->castVarRef()->varp()->subDTypep()->castIfaceRefDType()) { + pinp->v3error("Interface port '"<prettyName()<<"' is not connected to interface/modport pin expression"); + } else { + AstIfaceRefDType* pinIrefp = pinp->exprp()->castVarRef()->varp()->subDTypep()->castIfaceRefDType(); + //UINFO(9," pinIfaceRef "<ifaceViaCellp() != pinIrefp->ifaceViaCellp()) { + UINFO(9," IfaceRefDType needs reconnect "<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"< 0x"<<(uint32_t)varp<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 "<v3fatalSrc("parameter clone didn't hit AstIfaceRefDType"); + UINFO(8," IfaceClo "<ifacep(pinIrefp->ifaceViaCellp()); + UINFO(8," IfaceNew "<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 "<=10) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree")); } diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index fb3f661fb..479ed4fb1 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -66,6 +66,7 @@ struct V3ParseBisonYYSType { AstCell* cellp; AstConst* constp; AstMemberDType* memberp; + AstModportVarRef* modportvarrefp; AstNodeModule* modulep; AstNodeClassDType* classp; AstNodeDType* dtypep; diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index 827da0e27..3385f98dc 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -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 "<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 "<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); } diff --git a/src/V3SymTable.h b/src/V3SymTable.h index e8e7dd132..cef0ba171 100644 --- a/src/V3SymTable.h +++ b/src/V3SymTable.h @@ -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<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; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 34a03d426..ca64a072e 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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 "<iterateChildren(*this, vup); + nodep->dtypep(nodep); + nodep->widthForce(1, 1); // Not really relevant + UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed UINFO(5," NODECLASS "< yENDCLOCKING "endclocking" %token yENDFUNCTION "endfunction" %token yENDGENERATE "endgenerate" +%token yENDINTERFACE "endinterface" %token yENDMODULE "endmodule" %token yENDPACKAGE "endpackage" %token yENDPRIMITIVE "endprimitive" @@ -337,9 +338,11 @@ class AstSenTree; %token yINSIDE "inside" %token yINT "int" %token yINTEGER "integer" +%token yINTERFACE "interface" %token yLOCALPARAM "localparam" %token yLOGIC "logic" %token yLONGINT "longint" +%token yMODPORT "modport" %token yMODULE "module" %token yNAND "nand" %token 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: // ==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($3, $3, $4); PARSEP->instantCb($2, $2, $3, $4); PINNUMINC(); } - //UNSUP portDirNetE yINTERFACE idAny/*port*/ rangeListE sigAttrListE - //UNSUP { VARDTYPE($2); VARDONEA($3, $3, $4); PINNUMINC(); } - //UNSUP portDirNetE id/*interface*/ '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE - //UNSUP { VARDTYPE($2); VARDONEA($5, $5, $6); PARSEP->instantCb($2, $2, $5, $6); PINNUMINC(); } - //UNSUP portDirNetE yINTERFACE '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE - //UNSUP { VARDTYPE($2); VARDONEA($5, $5, $6); PINNUMINC(); } + portDirNetE id/*interface*/ idAny/*port*/ variable_dimensionListE sigAttrListE + { $$ = new AstPort($2,PINNUMINC(),*$3); + AstVar* varp=new AstVar($2,AstVarType(AstVarType::IFACEREF),*$3,VFlagChildDType(), + new AstIfaceRefDType($2,"",*$2)); + if ($4) varp->v3error("Unsupported: Arrayed interfaces"); + varp->addAttrsp($5); + $$->addNext(varp); } + | portDirNetE yINTERFACE idAny/*port*/ rangeListE sigAttrListE + { $2->v3error("Unsupported: virtual interfaces"); } + | portDirNetE id/*interface*/ '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE + { $$ = new AstPort($3,PINNUMINC(),*$5); + AstVar* varp=new AstVar($2,AstVarType(AstVarType::IFACEREF),*$5,VFlagChildDType(), + new AstIfaceRefDType($2,"",*$2,*$4)); + if ($6) varp->v3error("Unsupported: Arrayed interfaces"); + varp->addAttrsp($7); + $$->addNext(varp); } + | portDirNetE yINTERFACE '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE + { $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: // ==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: //********************************************************************** // 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: + yINTERFACE lifetimeE idAny/*new_interface*/ + { $$ = new AstIface($1,*$3); + $$->inLibrary(true); + PARSEP->rootp()->addModulep($$); + SYMP->pushNew($$); } + ; + +interface_itemListE: + /* empty */ { $$ = NULL; } + | interface_itemList { $$ = $1; } + ; + +interface_itemList: + interface_item { $$ = $1; } + | interface_itemList interface_item { $$ = $1->addNextNull($2); } + ; + +interface_item: // 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: // ==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: // ==IEEE: program_generate_item //UNSUP elaboration_system_task { $$ = $1; } ; +modport_declaration: // ==IEEE: modport_declaration + yMODPORT modport_itemList ';' { $$ = $2; } + ; + +modport_itemList: // IEEE: part of modport_declaration + modport_item { $$ = $1; } + | modport_itemList ',' modport_item { $$ = $1->addNextNull($3); } + ; + +modport_item: // ==IEEE: modport_item + id/*new-modport*/ '(' modportPortsDeclList ')' { $$ = new AstModport($2,*$1,$3); } + ; + +modportPortsDeclList: + 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: + // // IEEE: modport_simple_ports_declaration + port_direction modportSimplePort { $$ = new AstModportVarRef($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($1,*$1,AstVarType::INOUT); } + ; + +modportSimplePort: // IEEE: modport_simple_port or modport_tf_port, depending what keyword was earlier + id { $$ = $1; } + //UNSUP '.' idAny '(' ')' { } + //UNSUP '.' idAny '(' expr ')' { } + ; + //************************************************ // Variable Declarations diff --git a/test_regress/t/t_interface.pl b/test_regress/t/t_interface.pl index 3bd8e9cf6..f91289753 100755 --- a/test_regress/t/t_interface.pl +++ b/test_regress/t/t_interface.pl @@ -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 ( ); diff --git a/test_regress/t/t_interface.v b/test_regress/t/t_interface.v index 8dd7d9d25..1a9b8f0d7 100644 --- a/test_regress/t/t_interface.v +++ b/test_regress/t/t_interface.v @@ -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]; diff --git a/test_regress/t/t_interface_top.pl b/test_regress/t/t_interface1.pl similarity index 79% rename from test_regress/t/t_interface_top.pl rename to test_regress/t/t_interface1.pl index 1a62ab7b0..1118f2e0e 100755 --- a/test_regress/t/t_interface_top.pl +++ b/test_regress/t/t_interface1.pl @@ -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); diff --git a/test_regress/t/t_interface1.v b/test_regress/t/t_interface1.v new file mode 100644 index 000000000..3b3f9164a --- /dev/null +++ b/test_regress/t/t_interface1.v @@ -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 diff --git a/test_regress/t/t_interface1_modport.pl b/test_regress/t/t_interface1_modport.pl new file mode 100755 index 000000000..1118f2e0e --- /dev/null +++ b/test_regress/t/t_interface1_modport.pl @@ -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; diff --git a/test_regress/t/t_interface1_modport.v b/test_regress/t/t_interface1_modport.v new file mode 100644 index 000000000..2d41333bb --- /dev/null +++ b/test_regress/t/t_interface1_modport.v @@ -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 diff --git a/test_regress/t/t_interface2.pl b/test_regress/t/t_interface2.pl index 555a5a49b..7ae28e6f7 100755 --- a/test_regress/t/t_interface2.pl +++ b/test_regress/t/t_interface2.pl @@ -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 ( diff --git a/test_regress/t/t_interface2.v b/test_regress/t/t_interface2.v index c6c48d4ac..efe2611ab 100644 --- a/test_regress/t/t_interface2.v +++ b/test_regress/t/t_interface2.v @@ -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 diff --git a/test_regress/t/t_interface_down.pl b/test_regress/t/t_interface_down.pl new file mode 100755 index 000000000..1118f2e0e --- /dev/null +++ b/test_regress/t/t_interface_down.pl @@ -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; diff --git a/test_regress/t/t_interface_down.v b/test_regress/t/t_interface_down.v new file mode 100644 index 000000000..bec0b2d9b --- /dev/null +++ b/test_regress/t/t_interface_down.v @@ -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 diff --git a/test_regress/t/t_interface_down_gen.pl b/test_regress/t/t_interface_down_gen.pl new file mode 100755 index 000000000..c9f69ed2d --- /dev/null +++ b/test_regress/t/t_interface_down_gen.pl @@ -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; diff --git a/test_regress/t/t_interface_down_gen.v b/test_regress/t/t_interface_down_gen.v new file mode 100644 index 000000000..31117d357 --- /dev/null +++ b/test_regress/t/t_interface_down_gen.v @@ -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 diff --git a/test_regress/t/t_interface_down_inla.pl b/test_regress/t/t_interface_down_inla.pl new file mode 100755 index 000000000..f65c1e7f8 --- /dev/null +++ b/test_regress/t/t_interface_down_inla.pl @@ -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; diff --git a/test_regress/t/t_interface_down_inlab.pl b/test_regress/t/t_interface_down_inlab.pl new file mode 100755 index 000000000..f06160436 --- /dev/null +++ b/test_regress/t/t_interface_down_inlab.pl @@ -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; diff --git a/test_regress/t/t_interface_down_inlac.pl b/test_regress/t/t_interface_down_inlac.pl new file mode 100755 index 000000000..6aae82408 --- /dev/null +++ b/test_regress/t/t_interface_down_inlac.pl @@ -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; diff --git a/test_regress/t/t_interface_down_inlad.pl b/test_regress/t/t_interface_down_inlad.pl new file mode 100755 index 000000000..0addbdfe7 --- /dev/null +++ b/test_regress/t/t_interface_down_inlad.pl @@ -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; diff --git a/test_regress/t/t_interface_down_inlb.pl b/test_regress/t/t_interface_down_inlb.pl new file mode 100755 index 000000000..b8e22f240 --- /dev/null +++ b/test_regress/t/t_interface_down_inlb.pl @@ -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; diff --git a/test_regress/t/t_interface_down_inlbc.pl b/test_regress/t/t_interface_down_inlbc.pl new file mode 100755 index 000000000..53d009068 --- /dev/null +++ b/test_regress/t/t_interface_down_inlbc.pl @@ -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; diff --git a/test_regress/t/t_interface_down_inlbd.pl b/test_regress/t/t_interface_down_inlbd.pl new file mode 100755 index 000000000..bdbf3392c --- /dev/null +++ b/test_regress/t/t_interface_down_inlbd.pl @@ -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; diff --git a/test_regress/t/t_interface_down_inlc.pl b/test_regress/t/t_interface_down_inlc.pl new file mode 100755 index 000000000..f8ffc151e --- /dev/null +++ b/test_regress/t/t_interface_down_inlc.pl @@ -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; diff --git a/test_regress/t/t_interface_down_inlcd.pl b/test_regress/t/t_interface_down_inlcd.pl new file mode 100755 index 000000000..eb9218ec3 --- /dev/null +++ b/test_regress/t/t_interface_down_inlcd.pl @@ -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; diff --git a/test_regress/t/t_interface_down_inld.pl b/test_regress/t/t_interface_down_inld.pl new file mode 100755 index 000000000..c57e458a8 --- /dev/null +++ b/test_regress/t/t_interface_down_inld.pl @@ -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; diff --git a/test_regress/t/t_interface_gen.pl b/test_regress/t/t_interface_gen.pl new file mode 100755 index 000000000..1118f2e0e --- /dev/null +++ b/test_regress/t/t_interface_gen.pl @@ -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; diff --git a/test_regress/t/t_interface_gen.v b/test_regress/t/t_interface_gen.v new file mode 100644 index 000000000..37f0dc29e --- /dev/null +++ b/test_regress/t/t_interface_gen.v @@ -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 diff --git a/test_regress/t/t_interface_gen2.pl b/test_regress/t/t_interface_gen2.pl new file mode 100755 index 000000000..1118f2e0e --- /dev/null +++ b/test_regress/t/t_interface_gen2.pl @@ -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; diff --git a/test_regress/t/t_interface_gen2.v b/test_regress/t/t_interface_gen2.v new file mode 100644 index 000000000..13f0acbd5 --- /dev/null +++ b/test_regress/t/t_interface_gen2.v @@ -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 diff --git a/test_regress/t/t_interface_gen3.pl b/test_regress/t/t_interface_gen3.pl new file mode 100755 index 000000000..1118f2e0e --- /dev/null +++ b/test_regress/t/t_interface_gen3.pl @@ -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; diff --git a/test_regress/t/t_interface_gen3.v b/test_regress/t/t_interface_gen3.v new file mode 100644 index 000000000..e3c107ca8 --- /dev/null +++ b/test_regress/t/t_interface_gen3.v @@ -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 diff --git a/test_regress/t/t_interface_inl.pl b/test_regress/t/t_interface_inl.pl index 860bef48e..ef9d007ba 100755 --- a/test_regress/t/t_interface_inl.pl +++ b/test_regress/t/t_interface_inl.pl @@ -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 ( diff --git a/test_regress/t/t_interface_mismodport_bad.pl b/test_regress/t/t_interface_mismodport_bad.pl new file mode 100755 index 000000000..3ff2dbd57 --- /dev/null +++ b/test_regress/t/t_interface_mismodport_bad.pl @@ -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; diff --git a/test_regress/t/t_interface_mismodport_bad.v b/test_regress/t/t_interface_mismodport_bad.v new file mode 100644 index 000000000..ad8aa2a52 --- /dev/null +++ b/test_regress/t/t_interface_mismodport_bad.v @@ -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 diff --git a/test_regress/t/t_interface_modport.pl b/test_regress/t/t_interface_modport.pl index d39b00a42..1118f2e0e 100755 --- a/test_regress/t/t_interface_modport.pl +++ b/test_regress/t/t_interface_modport.pl @@ -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 ( diff --git a/test_regress/t/t_interface_modport.v b/test_regress/t/t_interface_modport.v index b1479e2fc..9544463d0 100644 --- a/test_regress/t/t_interface_modport.v +++ b/test_regress/t/t_interface_modport.v @@ -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 diff --git a/test_regress/t/t_interface_modport_inl.pl b/test_regress/t/t_interface_modport_inl.pl index 37f649962..57289dfd8 100755 --- a/test_regress/t/t_interface_modport_inl.pl +++ b/test_regress/t/t_interface_modport_inl.pl @@ -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 ( diff --git a/test_regress/t/t_interface_top_bad.pl b/test_regress/t/t_interface_top_bad.pl new file mode 100755 index 000000000..34abb76b0 --- /dev/null +++ b/test_regress/t/t_interface_top_bad.pl @@ -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; diff --git a/test_regress/t/t_interface_top.v b/test_regress/t/t_interface_top_bad.v similarity index 64% rename from test_regress/t/t_interface_top.v rename to test_regress/t/t_interface_top_bad.v index d2aaccc30..2334789b0 100644 --- a/test_regress/t/t_interface_top.v +++ b/test_regress/t/t_interface_top_bad.v @@ -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; diff --git a/test_regress/t/t_lint_implicit_port.pl b/test_regress/t/t_lint_implicit_port.pl index bd5d46c3e..a3f299571 100755 --- a/test_regress/t/t_lint_implicit_port.pl +++ b/test_regress/t/t_lint_implicit_port.pl @@ -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); diff --git a/test_regress/t/t_mem_slice.pl b/test_regress/t/t_mem_slice.pl index 639bb98f5..7058e622f 100755 --- a/test_regress/t/t_mem_slice.pl +++ b/test_regress/t/t_mem_slice.pl @@ -8,7 +8,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Version 2.0. compile ( - v_flags => [], ); execute (