mirror of
https://github.com/verilator/verilator.git
synced 2025-07-31 07:56:10 +00:00
Support interfaces and modports, bug102.
This commit is contained in:
parent
7c834ad118
commit
23bb045a72
2
Changes
2
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]
|
||||
|
@ -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
|
||||
|
11
src/V3Ast.h
11
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
|
||||
|
@ -35,6 +35,17 @@
|
||||
// Special methods
|
||||
|
||||
// We need these here, because the classes they point to aren't defined when we declare the class
|
||||
const char* AstIfaceRefDType::broken() const {
|
||||
BROKEN_RTN(m_ifacep && !m_ifacep->brokeExists());
|
||||
BROKEN_RTN(m_cellp && !m_cellp->brokeExists());
|
||||
BROKEN_RTN(m_modportp && !m_modportp->brokeExists());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AstIface* AstIfaceRefDType::ifaceViaCellp() const {
|
||||
return ((m_cellp && m_cellp->modp()) ? m_cellp->modp()->castIface() : m_ifacep);
|
||||
}
|
||||
|
||||
const char* AstNodeVarRef::broken() const {
|
||||
BROKEN_RTN(m_varScopep && !m_varScopep->brokeExists());
|
||||
BROKEN_RTN(m_varp && !m_varp->brokeExists());
|
||||
@ -723,12 +734,31 @@ void AstEnumItemRef::dump(ostream& str) {
|
||||
if (itemp()) { itemp()->dump(str); }
|
||||
else { str<<"UNLINKED"; }
|
||||
}
|
||||
void AstIfaceRefDType::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (cellName()!="") { str<<" cell="<<cellName(); }
|
||||
if (ifaceName()!="") { str<<" if="<<ifaceName(); }
|
||||
if (modportName()!="") { str<<" mp="<<modportName(); }
|
||||
if (cellp()) { str<<" -> "; cellp()->dump(str); }
|
||||
else if (ifacep()) { str<<" -> "; ifacep()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstIfaceRefDType::dumpSmall(ostream& str) {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"iface";
|
||||
}
|
||||
void AstJumpGo::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" -> ";
|
||||
if (labelp()) { labelp()->dump(str); }
|
||||
else { str<<"%Error:UNLINKED"; }
|
||||
}
|
||||
void AstModportVarRef::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" "<<varType();
|
||||
if (varp()) { str<<" -> "; varp()->dump(str); }
|
||||
else { str<<" -> UNLINKED"; }
|
||||
}
|
||||
void AstPin::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
if (modVarp()) { str<<" -> "; modVarp()->dump(str); }
|
||||
|
105
src/V3AstNodes.h
105
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;
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -190,6 +190,7 @@ private:
|
||||
<<"' does not match "<<nodep->typeName()<<" name: "<<nodep->prettyName());
|
||||
}
|
||||
}
|
||||
if (nodep->castIface() || nodep->castPackage()) nodep->inLibrary(true); // Interfaces can't be at top, unless asked
|
||||
bool topMatch = (v3Global.opt.topModule()==nodep->prettyName());
|
||||
if (topMatch) {
|
||||
m_topVertexp = vertex(nodep);
|
||||
@ -210,6 +211,25 @@ private:
|
||||
m_modp = NULL;
|
||||
}
|
||||
|
||||
virtual void visit(AstIfaceRefDType* nodep, AstNUser*) {
|
||||
// Cell: Resolve its filename. If necessary, parse it.
|
||||
UINFO(4,"Link IfaceRef: "<<nodep<<endl);
|
||||
// Use findIdUpward instead of findIdFlat; it doesn't matter for now
|
||||
// but we might support modules-under-modules someday.
|
||||
AstNodeModule* modp = resolveModule(nodep, nodep->ifaceName());
|
||||
if (modp) {
|
||||
if (modp->castIface()) {
|
||||
// Track module depths, so can sort list from parent down to children
|
||||
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(modp), 1, false);
|
||||
if (!nodep->cellp()) nodep->ifacep(modp->castIface());
|
||||
} else if (modp->castNotFoundModule()) { // Will error out later
|
||||
} else {
|
||||
nodep->v3error("Non-interface used as an interface: "<<nodep->prettyName());
|
||||
}
|
||||
}
|
||||
// Note cannot do modport resolution here; modports are allowed underneath generates
|
||||
}
|
||||
|
||||
virtual void visit(AstPackageImport* nodep, AstNUser*) {
|
||||
// Package Import: We need to do the package before the use of a package
|
||||
nodep->iterateChildren(*this);
|
||||
@ -314,6 +334,23 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nodep->modp()->castIface()) {
|
||||
// Cell really is the parent's instantiation of an interface, not a normal module
|
||||
// Make sure we have a variable to refer to this cell, so can <ifacename>.<innermember>
|
||||
// in the same way that a child does. Rename though to avoid conflict with cell.
|
||||
// This is quite similar to how classes work; when unpacked classes are better supported
|
||||
// may remap interfaces to be more like a class.
|
||||
if (!nodep->hasIfaceVar()) {
|
||||
string varName = nodep->name()+"__Viftop"; // V3LinkDot looks for this naming
|
||||
AstIfaceRefDType* idtypep = new AstIfaceRefDType(nodep->fileline(), nodep->name(), nodep->modp()->name());
|
||||
idtypep->cellp(nodep); // Only set when real parent cell known
|
||||
idtypep->ifacep(NULL); // cellp overrides
|
||||
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep);
|
||||
varp->isIfaceParent(true);
|
||||
nodep->addNextHere(varp);
|
||||
nodep->hasIfaceVar(true);
|
||||
}
|
||||
}
|
||||
if (nodep->modp()) {
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
@ -31,6 +31,26 @@
|
||||
// VarXRef/Func's:
|
||||
// Find appropriate named cell and link to var they reference
|
||||
//*************************************************************************
|
||||
// Interfaces:
|
||||
// CELL (.port (ifref)
|
||||
// ^--- cell -> IfaceDTypeRef(iface)
|
||||
// ^--- cell.modport -> IfaceDTypeRef(iface,modport)
|
||||
// ^--- varref(input_ifref) -> IfaceDTypeRef(iface)
|
||||
// ^--- varref(input_ifref).modport -> IfaceDTypeRef(iface,modport)
|
||||
// FindVisitor:
|
||||
// #1: Insert interface Vars
|
||||
// #2: Insert ModPort names
|
||||
// IfaceVisitor:
|
||||
// #3: Update ModPortVarRef to point at interface vars (after #1)
|
||||
// #4: Create ModPortVarRef symbol table entries
|
||||
// FindVisitor-insertIfaceRefs()
|
||||
// #5: Resolve IfaceRefDtype modport names (after #2)
|
||||
// #7: Record sym of IfaceRefDType and aliased interface and/or modport (after #4,#5)
|
||||
// insertAllScopeAliases():
|
||||
// #8: Insert modport's symbols under IfaceRefDType (after #7)
|
||||
// ResolveVisitor:
|
||||
// #9: Resolve general variables, which may point into the interface or modport (after #8)
|
||||
//*************************************************************************
|
||||
// TOP
|
||||
// {name-of-top-modulename}
|
||||
// a (VSymEnt->AstCell)
|
||||
@ -77,8 +97,11 @@ private:
|
||||
AstUser4InUse m_inuser4;
|
||||
|
||||
// TYPES
|
||||
typedef std::multimap<string,VSymEnt*> NameScopeSymMap;
|
||||
typedef set <pair<AstNodeModule*,string> > ImplicitNameSet;
|
||||
typedef multimap<string,VSymEnt*> NameScopeSymMap;
|
||||
typedef map<VSymEnt*,VSymEnt*> ScopeAliasMap;
|
||||
typedef set<pair<AstNodeModule*,string> > ImplicitNameSet;
|
||||
typedef vector<VSymEnt*> IfaceVarSyms;
|
||||
typedef vector<pair<AstIface*,VSymEnt*> > IfaceModSyms;
|
||||
|
||||
static LinkDotState* s_errorThisp; // Last self, for error reporting only
|
||||
|
||||
@ -87,6 +110,9 @@ private:
|
||||
VSymEnt* m_dunitEntp; // $unit entry
|
||||
NameScopeSymMap m_nameScopeSymMap; // Map of scope referenced by non-pretty textual name
|
||||
ImplicitNameSet m_implicitNameSet; // For [module][signalname] if we can implicitly create it
|
||||
ScopeAliasMap m_scopeAliasMap; // Map of <lhs,rhs> aliases
|
||||
IfaceVarSyms m_ifaceVarSyms; // List of AstIfaceRefDType's to be imported
|
||||
IfaceModSyms m_ifaceModSyms; // List of AstIface+Symbols to be processed
|
||||
bool m_forPrimary; // First link
|
||||
bool m_forPrearray; // Compress cell__[array] refs
|
||||
bool m_forScopeCreation; // Remove VarXRefs for V3Scope
|
||||
@ -105,6 +131,10 @@ public:
|
||||
if (logp->fail()) v3fatalSrc("Can't write "<<filename);
|
||||
ostream& os = *logp;
|
||||
m_syms.dump(os);
|
||||
if (!m_scopeAliasMap.empty()) os<<"\nScopeAliasMap:\n";
|
||||
for (ScopeAliasMap::iterator it = m_scopeAliasMap.begin(); it != m_scopeAliasMap.end(); ++it) {
|
||||
os<<"\t"<<it->first<<" -> "<<it->second<<endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void preErrorDumpHandler() {
|
||||
@ -148,6 +178,7 @@ public:
|
||||
else if (nodep->castTask()) return "task";
|
||||
else if (nodep->castFunc()) return "function";
|
||||
else if (nodep->castBegin()) return "block";
|
||||
else if (nodep->castIface()) return "interface";
|
||||
else return nodep->prettyTypeName();
|
||||
}
|
||||
static string ucfirst(const string& text) {
|
||||
@ -314,6 +345,69 @@ public:
|
||||
&& (m_implicitNameSet.find(make_pair(nodep,varname)) != m_implicitNameSet.end());
|
||||
}
|
||||
|
||||
// Track and later recurse interface modules
|
||||
void insertIfaceModSym(AstIface* nodep, VSymEnt* symp) {
|
||||
m_ifaceModSyms.push_back(make_pair(nodep, symp));
|
||||
}
|
||||
void computeIfaceModSyms();
|
||||
|
||||
// Track and later insert interface references
|
||||
void insertIfaceVarSym(VSymEnt* symp) { // Where sym is for a VAR of dtype IFACEREFDTYPE
|
||||
m_ifaceVarSyms.push_back(symp);
|
||||
}
|
||||
void computeIfaceVarSyms() {
|
||||
for (IfaceVarSyms::iterator it = m_ifaceVarSyms.begin(); it != m_ifaceVarSyms.end(); ++it) {
|
||||
VSymEnt* varSymp = *it;
|
||||
AstVar* varp = varSymp->nodep()->castVar();
|
||||
UINFO(9, " insAllIface se"<<(void*)varSymp<<" "<<varp<<endl);
|
||||
AstIfaceRefDType* ifacerefp = varp->subDTypep()->castIfaceRefDType();
|
||||
if (!ifacerefp) varp->v3fatalSrc("Non-ifacerefs on list!");
|
||||
if (!ifacerefp->ifaceViaCellp()) ifacerefp->v3fatalSrc("Unlinked interface");
|
||||
VSymEnt* ifaceSymp = getNodeSym(ifacerefp->ifaceViaCellp());
|
||||
VSymEnt* ifOrPortSymp = ifaceSymp;
|
||||
// Link Modport names to the Modport Node under the Interface
|
||||
if (ifacerefp->isModport()) {
|
||||
VSymEnt* foundp = ifaceSymp->findIdFallback(ifacerefp->modportName());
|
||||
bool ok = false;
|
||||
if (foundp) {
|
||||
if (AstModport* modportp = foundp->nodep()->castModport()) {
|
||||
UINFO(4,"Link Modport: "<<modportp<<endl);
|
||||
ifacerefp->modportp(modportp);
|
||||
ifOrPortSymp = foundp;
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if (!ok) ifacerefp->v3error("Modport not found under interface '"
|
||||
<<ifacerefp->prettyName(ifacerefp->ifaceName())
|
||||
<<"': "<<ifacerefp->prettyName(ifacerefp->modportName()));
|
||||
}
|
||||
// Alias won't expand until interfaces and modport names are known; see notes at top
|
||||
insertScopeAlias(varSymp, ifOrPortSymp);
|
||||
}
|
||||
m_ifaceVarSyms.clear();
|
||||
}
|
||||
|
||||
// Track and later insert scope aliases
|
||||
void insertScopeAlias(VSymEnt* lhsp, VSymEnt* rhsp) { // Typically lhsp=VAR w/dtype IFACEREF, rhsp=IFACE cell
|
||||
UINFO(9," insertScopeAlias se"<<(void*)lhsp<<" se"<<(void*)rhsp<<endl);
|
||||
m_scopeAliasMap.insert(make_pair(lhsp, rhsp));
|
||||
}
|
||||
void computeScopeAliases() {
|
||||
UINFO(9,"computeIfaceAliases\n");
|
||||
for (ScopeAliasMap::iterator it=m_scopeAliasMap.begin(); it!=m_scopeAliasMap.end(); ++it) {
|
||||
VSymEnt* lhsp = it->first;
|
||||
VSymEnt* srcp = lhsp;
|
||||
while (1) { // Follow chain of aliases up to highest level non-alias
|
||||
ScopeAliasMap::iterator it2 = m_scopeAliasMap.find(srcp);
|
||||
if (it2 != m_scopeAliasMap.end()) { srcp = it2->second; continue; }
|
||||
else break;
|
||||
}
|
||||
UINFO(9," iiasa: Insert alias se"<<lhsp<<" <- se"<<srcp<<" "<<srcp->nodep()<<endl);
|
||||
// srcp should be an interface reference pointing to the interface we want to import
|
||||
lhsp->importFromIface(symsp(), srcp);
|
||||
}
|
||||
m_scopeAliasMap.clear();
|
||||
}
|
||||
private:
|
||||
VSymEnt* findWithAltFallback(VSymEnt* symp, const string& name, const string& altname) {
|
||||
VSymEnt* findp = symp->findIdFallback(name);
|
||||
@ -503,6 +597,10 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
// Iterate
|
||||
nodep->iterateChildren(*this);
|
||||
nodep->user4(true);
|
||||
// Interfaces need another pass when signals are resolved
|
||||
if (AstIface* ifacep = nodep->castIface()) {
|
||||
m_statep->insertIfaceModSym(ifacep, m_curSymp);
|
||||
}
|
||||
} else { //!doit
|
||||
// Will be optimized away later
|
||||
// Can't remove now, as our backwards iterator will throw up
|
||||
@ -719,12 +817,16 @@ class LinkDotFindVisitor : public AstNVisitor {
|
||||
}
|
||||
}
|
||||
if (ins) {
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
|
||||
VSymEnt* insp = m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_packagep);
|
||||
if (m_statep->forPrimary() && nodep->isGParam()) {
|
||||
m_paramNum++;
|
||||
VSymEnt* symp = m_statep->insertSym(m_curSymp, "__paramNumber"+cvtToStr(m_paramNum), nodep, m_packagep);
|
||||
symp->exported(false);
|
||||
}
|
||||
if (nodep->subDTypep()->castIfaceRefDType()) {
|
||||
// Can't resolve until interfaces and modport names are known; see notes at top
|
||||
m_statep->insertIfaceVarSym(insp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -899,8 +1001,8 @@ private:
|
||||
AstVar* refp = foundp->nodep()->castVar();
|
||||
if (!refp) {
|
||||
nodep->v3error("Input/output/inout declaration not found for port: "<<nodep->prettyName());
|
||||
} else if (!refp->isIO()) {
|
||||
nodep->v3error("Pin is not an in/out/inout: "<<nodep->prettyName());
|
||||
} else if (!refp->isIO() && !refp->isIfaceRef()) {
|
||||
nodep->v3error("Pin is not an in/out/inout/interface: "<<nodep->prettyName());
|
||||
} else {
|
||||
refp->user4(true);
|
||||
VSymEnt* symp = m_statep->insertSym(m_statep->getNodeSym(m_modp),
|
||||
@ -956,9 +1058,10 @@ public:
|
||||
//======================================================================
|
||||
|
||||
class LinkDotScopeVisitor : public AstNVisitor {
|
||||
private:
|
||||
|
||||
// STATE
|
||||
LinkDotState* m_statep; // State to pass between visitors, including symbol table
|
||||
AstScope* m_scopep; // The current scope
|
||||
VSymEnt* m_modSymp; // Symbol entry for current module
|
||||
|
||||
int debug() { return LinkDotState::debug(); }
|
||||
@ -974,12 +1077,36 @@ private:
|
||||
// Using the CELL names, we created all hierarchy. We now need to match this Scope
|
||||
// up with the hierarchy created by the CELL names.
|
||||
m_modSymp = m_statep->getScopeSym(nodep);
|
||||
m_scopep = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
m_modSymp = NULL;
|
||||
m_scopep = NULL;
|
||||
}
|
||||
virtual void visit(AstVarScope* nodep, AstNUser*) {
|
||||
if (!nodep->varp()->isFuncLocal()) {
|
||||
m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep, NULL);
|
||||
VSymEnt* varSymp = m_statep->insertSym(m_modSymp, nodep->varp()->name(), nodep, NULL);
|
||||
if (nodep->varp()->isIfaceRef()
|
||||
&& nodep->varp()->isIfaceParent()) {
|
||||
UINFO(9,"Iface parent ref var "<<nodep->varp()->name()<<" "<<nodep<<endl);
|
||||
// Find the interface cell the var references
|
||||
AstIfaceRefDType* dtypep = nodep->varp()->dtypep()->castIfaceRefDType();
|
||||
if (!dtypep) nodep->v3fatalSrc("Non AstIfaceRefDType on isIfaceRef() var");
|
||||
UINFO(9,"Iface parent dtype "<<dtypep<<endl);
|
||||
string ifcellname = dtypep->cellName();
|
||||
string baddot; VSymEnt* okSymp;
|
||||
VSymEnt* cellSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp);
|
||||
if (!cellSymp) nodep->v3fatalSrc("No symbol for interface cell: " <<nodep->prettyName(ifcellname));
|
||||
UINFO(5, " Found interface cell: se"<<(void*)cellSymp<<" "<<cellSymp->nodep()<<endl);
|
||||
if (dtypep->modportName()!="") {
|
||||
VSymEnt* mpSymp = m_statep->findDotted(m_modSymp, ifcellname, baddot, okSymp);
|
||||
if (!mpSymp) { nodep->v3fatalSrc("No symbol for interface modport: " <<nodep->prettyName(dtypep->modportName())); }
|
||||
else cellSymp = mpSymp;
|
||||
UINFO(5, " Found modport cell: se"<<(void*)cellSymp<<" "<<mpSymp->nodep()<<endl);
|
||||
}
|
||||
// Interface reference; need to put whole thing into symtable, but can't clone it now
|
||||
// as we may have a later alias for it.
|
||||
m_statep->insertScopeAlias(varSymp, cellSymp);
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
||||
@ -997,6 +1124,37 @@ private:
|
||||
fromVscp->user2p(toVscp);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
|
||||
UINFO(5,"ASSIGNVARSCOPE "<<nodep<<endl);
|
||||
if (debug()>=9) nodep->dumpTree(cout,"-\t\t\t\tavs: ");
|
||||
VSymEnt* rhsSymp;
|
||||
{
|
||||
AstVarRef* refp = nodep->rhsp()->castVarRef();
|
||||
if (!refp) nodep->v3fatalSrc("Unsupported: Non VarRef attached to interface pin");
|
||||
string scopename = refp->name();
|
||||
string baddot; VSymEnt* okSymp;
|
||||
VSymEnt* symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp);
|
||||
if (!symp) nodep->v3fatalSrc("No symbol for interface alias rhs");
|
||||
UINFO(5, " Found a linked scope RHS: "<<scopename<<" se"<<(void*)symp<<" "<<symp->nodep()<<endl);
|
||||
rhsSymp = symp;
|
||||
}
|
||||
VSymEnt* lhsSymp;
|
||||
{
|
||||
AstVarXRef* refp = nodep->lhsp()->castVarXRef();
|
||||
if (!refp) nodep->v3fatalSrc("Unsupported: Non VarXRef attached to interface pin");
|
||||
string scopename = refp->dotted()+"."+refp->name();
|
||||
string baddot; VSymEnt* okSymp;
|
||||
VSymEnt* symp = m_statep->findDotted(m_modSymp, scopename, baddot, okSymp);
|
||||
if (!symp) nodep->v3fatalSrc("No symbol for interface alias lhs");
|
||||
UINFO(5, " Found a linked scope LHS: "<<scopename<<" se"<<(void*)symp<<" "<<symp->nodep()<<endl);
|
||||
lhsSymp = symp;
|
||||
}
|
||||
// Remember the alias - can't do it yet because we may have additional symbols to be added,
|
||||
// or maybe an alias of an alias
|
||||
m_statep->insertScopeAlias(lhsSymp, rhsSymp);
|
||||
// We have stored the link, we don't need these any more
|
||||
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
||||
}
|
||||
// For speed, don't recurse things that can't have scope
|
||||
// Note we allow AstNodeStmt's as generates may be under them
|
||||
virtual void visit(AstCell*, AstNUser*) {}
|
||||
@ -1012,6 +1170,7 @@ public:
|
||||
LinkDotScopeVisitor(AstNetlist* rootp, LinkDotState* statep) {
|
||||
UINFO(4,__FUNCTION__<<": "<<endl);
|
||||
m_modSymp = NULL;
|
||||
m_scopep = NULL;
|
||||
m_statep = statep;
|
||||
//
|
||||
rootp->accept(*this);
|
||||
@ -1021,6 +1180,78 @@ public:
|
||||
|
||||
//======================================================================
|
||||
|
||||
// Iterate an interface to resolve modports
|
||||
class LinkDotIfaceVisitor : public AstNVisitor {
|
||||
// STATE
|
||||
LinkDotState* m_statep; // State to pass between visitors, including symbol table
|
||||
VSymEnt* m_curSymp; // Symbol Entry for current table, where to lookup/insert
|
||||
|
||||
// METHODS
|
||||
int debug() { return LinkDotState::debug(); }
|
||||
|
||||
// VISITs
|
||||
virtual void visit(AstModport* nodep, AstNUser*) {
|
||||
// Modport: Remember its name for later resolution
|
||||
UINFO(5," fiv: "<<nodep<<endl);
|
||||
VSymEnt* oldCurSymp = m_curSymp;
|
||||
{
|
||||
// Create symbol table for the vars
|
||||
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, NULL);
|
||||
m_curSymp->fallbackp(oldCurSymp);
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
m_curSymp = oldCurSymp;
|
||||
}
|
||||
virtual void visit(AstModportVarRef* nodep, AstNUser*) {
|
||||
UINFO(5," fiv: "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
VSymEnt* symp = m_curSymp->findIdFallback(nodep->name());
|
||||
if (!symp) {
|
||||
nodep->v3error("Modport item not found: "<<nodep->prettyName());
|
||||
} else if (AstVar* varp = symp->nodep()->castVar()) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
nodep->varp(varp);
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), varp, NULL/*package*/);
|
||||
} else if (AstVarScope* vscp = symp->nodep()->castVarScope()) {
|
||||
// Make symbol under modport that points at the _interface_'s var, not the modport.
|
||||
nodep->varp(vscp->varp());
|
||||
m_statep->insertSym(m_curSymp, nodep->name(), vscp, NULL/*package*/);
|
||||
} else {
|
||||
nodep->v3error("Modport item is not a variable: "<<nodep->prettyName());
|
||||
}
|
||||
if (m_statep->forScopeCreation()) {
|
||||
// Done with AstModportVarRef.
|
||||
// Delete to prevent problems if we dead-delete pointed to variable
|
||||
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNode* nodep, AstNUser*) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
LinkDotIfaceVisitor(AstIface* nodep, VSymEnt* curSymp, LinkDotState* statep) {
|
||||
UINFO(4,__FUNCTION__<<": "<<endl);
|
||||
m_curSymp = curSymp;
|
||||
m_statep = statep;
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~LinkDotIfaceVisitor() {}
|
||||
};
|
||||
|
||||
void LinkDotState::computeIfaceModSyms() {
|
||||
for (IfaceModSyms::iterator it=m_ifaceModSyms.begin(); it!=m_ifaceModSyms.end(); ++it) {
|
||||
AstIface* nodep = it->first;
|
||||
VSymEnt* symp = it->second;
|
||||
LinkDotIfaceVisitor(nodep, symp, this);
|
||||
}
|
||||
m_ifaceModSyms.clear();
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
|
||||
class LinkDotResolveVisitor : public AstNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
@ -1049,6 +1280,7 @@ private:
|
||||
AstCell* m_cellp; // Current cell
|
||||
AstNodeModule* m_modp; // Current module
|
||||
AstNodeFTask* m_ftaskp; // Current function/task
|
||||
int m_modportNum; // Uniqueify modport numbers
|
||||
|
||||
struct DotStates {
|
||||
DotPosition m_dotPos; // Scope part of dotted resolution
|
||||
@ -1104,6 +1336,16 @@ private:
|
||||
m_ds.m_dotErr = true;
|
||||
}
|
||||
}
|
||||
AstVar* makeIfaceModportVar(FileLine* fl, AstCell* cellp, AstIface* ifacep, AstModport* modportp) {
|
||||
// Create iface variable, using duplicate var when under same module scope
|
||||
string varName = ifacep->name()+"__Vmp__"+modportp->name()+"__Viftop"+cvtToStr(++m_modportNum);
|
||||
AstIfaceRefDType* idtypep = new AstIfaceRefDType(fl, cellp->name(), ifacep->name(), modportp->name());
|
||||
idtypep->cellp(cellp);
|
||||
AstVar* varp = new AstVar(fl, AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep);
|
||||
varp->isIfaceParent(true);
|
||||
m_modp->addStmtp(varp);
|
||||
return varp;
|
||||
}
|
||||
|
||||
// VISITs
|
||||
virtual void visit(AstNetlist* nodep, AstNUser* vup) {
|
||||
@ -1119,6 +1361,7 @@ private:
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getNodeSym(nodep); // Until overridden by a SCOPE
|
||||
m_cellp = NULL;
|
||||
m_modp = nodep;
|
||||
m_modportNum = 0;
|
||||
nodep->iterateChildren(*this);
|
||||
m_modp = NULL;
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = NULL;
|
||||
@ -1183,8 +1426,8 @@ private:
|
||||
return;
|
||||
}
|
||||
nodep->v3error("Pin not found: "<<nodep->prettyName());
|
||||
} else if (!refp->isIO() && !refp->isParam()) {
|
||||
nodep->v3error("Pin is not an in/out/inout/param: "<<nodep->prettyName());
|
||||
} else if (!refp->isIO() && !refp->isParam() && !refp->isIfaceRef()) {
|
||||
nodep->v3error("Pin is not an in/out/inout/param/interface: "<<nodep->prettyName());
|
||||
} else {
|
||||
nodep->modVarp(refp);
|
||||
if (refp->user5p() && refp->user5p()->castNode()!=nodep) {
|
||||
@ -1326,9 +1569,41 @@ private:
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
// Upper AstDot visitor will handle it from here
|
||||
}
|
||||
else if (foundp->nodep()->castCell()
|
||||
&& allowVar && m_cellp
|
||||
&& foundp->nodep()->castCell()->modp()->castIface()) {
|
||||
// Interfaces can be referenced like a variable for interconnect
|
||||
AstCell* cellp = foundp->nodep()->castCell();
|
||||
VSymEnt* cellEntp = m_statep->getNodeSym(cellp); if (!cellEntp) nodep->v3fatalSrc("No interface sym entry");
|
||||
VSymEnt* parentEntp = cellEntp->parentp(); // Container of the var; probably a module or generate begin
|
||||
string findName = nodep->name()+"__Viftop";
|
||||
AstVar* ifaceRefVarp = parentEntp->findIdFallback(findName)->nodep()->castVar();
|
||||
if (!ifaceRefVarp) nodep->v3fatalSrc("Can't find interface var ref: "<<findName);
|
||||
//
|
||||
ok = true;
|
||||
if (m_ds.m_dotText!="") m_ds.m_dotText += ".";
|
||||
m_ds.m_dotText += nodep->name();
|
||||
m_ds.m_dotSymp = foundp;
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
UINFO(9," cell -> iface varref "<<foundp->nodep()<<endl);
|
||||
AstNode* newp = new AstVarRef(ifaceRefVarp->fileline(), ifaceRefVarp, false);
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); nodep = NULL;
|
||||
}
|
||||
}
|
||||
else if (AstVar* varp = foundp->nodep()->castVar()) {
|
||||
if (allowVar) {
|
||||
if (AstIfaceRefDType* ifacerefp = varp->subDTypep()->castIfaceRefDType()) {
|
||||
if (!ifacerefp->ifaceViaCellp()) ifacerefp->v3fatalSrc("Unlinked interface");
|
||||
// Really this is a scope reference into an interface
|
||||
UINFO(9,"varref-ifaceref "<<m_ds.m_dotText<<" "<<nodep<<endl);
|
||||
if (m_ds.m_dotText!="") m_ds.m_dotText += ".";
|
||||
m_ds.m_dotText += nodep->name();
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(ifacerefp->ifaceViaCellp());
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
ok = true;
|
||||
AstNode* newp = new AstVarRef(nodep->fileline(), varp, false);
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); nodep = NULL;
|
||||
}
|
||||
else if (allowVar) {
|
||||
AstNodeVarRef* newp;
|
||||
if (m_ds.m_dotText != "") {
|
||||
newp = new AstVarXRef(nodep->fileline(), nodep->name(), m_ds.m_dotText, false); // lvalue'ness computed later
|
||||
@ -1345,6 +1620,34 @@ private:
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
else if (AstModport* modportp = foundp->nodep()->castModport()) {
|
||||
// A scope reference into an interface's modport (not necessarily at a pin connection)
|
||||
UINFO(9,"cell-ref-to-modport "<<m_ds.m_dotText<<" "<<nodep<<endl);
|
||||
UINFO(9,"dotSymp "<<m_ds.m_dotSymp<<" "<<m_ds.m_dotSymp->nodep()<<endl);
|
||||
// Iface was the previously dotted component
|
||||
if (!m_ds.m_dotSymp
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) {
|
||||
nodep->v3error("Modport not referenced as <interface>."<<modportp->prettyName());
|
||||
} else if (!m_ds.m_dotSymp->nodep()->castCell()->modp()
|
||||
|| !m_ds.m_dotSymp->nodep()->castCell()->modp()->castIface()) {
|
||||
nodep->v3error("Modport not referenced from underneath an interface: "<<modportp->prettyName());
|
||||
} else {
|
||||
AstCell* cellp = m_ds.m_dotSymp->nodep()->castCell();
|
||||
if (!cellp) nodep->v3fatalSrc("Modport not referenced from a cell");
|
||||
AstIface* ifacep = cellp->modp()->castIface();
|
||||
//string cellName = m_ds.m_dotText; // Use cellp->name
|
||||
if (m_ds.m_dotText!="") m_ds.m_dotText += ".";
|
||||
m_ds.m_dotText += nodep->name();
|
||||
m_ds.m_dotSymp = m_statep->getNodeSym(modportp);
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
ok = true;
|
||||
AstVar* varp = makeIfaceModportVar(nodep->fileline(), cellp, ifacep, modportp);
|
||||
AstVarRef* refp = new AstVarRef(varp->fileline(), varp, false);
|
||||
nodep->replaceWith(refp); pushDeletep(nodep); nodep = NULL;
|
||||
}
|
||||
}
|
||||
else if (AstEnumItem* valuep = foundp->nodep()->castEnumItem()) {
|
||||
if (allowVar) {
|
||||
AstNode* newp = new AstEnumItemRef(nodep->fileline(), valuep, foundp->packagep());
|
||||
@ -1677,6 +1980,7 @@ public:
|
||||
m_cellp = NULL;
|
||||
m_modp = NULL;
|
||||
m_ftaskp = NULL;
|
||||
m_modportNum = 0;
|
||||
//
|
||||
rootp->accept(*this);
|
||||
}
|
||||
@ -1698,12 +2002,18 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) {
|
||||
LinkDotParamVisitor visitors(rootp,&state);
|
||||
if (LinkDotState::debug()>=5 || v3Global.opt.dumpTree()>=9) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-param.tree"));
|
||||
}
|
||||
else if (step == LDS_ARRAYED) {}
|
||||
else if (step == LDS_SCOPED) {
|
||||
// Well after the initial link when we're ready to operate on the flat design,
|
||||
// process AstScope's. This needs to be separate pass after whole hierarchy graph created.
|
||||
LinkDotScopeVisitor visitors(rootp,&state);
|
||||
if (LinkDotState::debug()>=5 || v3Global.opt.dumpTree()>=9) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("prelinkdot-scoped.tree"));
|
||||
}
|
||||
else v3fatalSrc("Bad case");
|
||||
state.dump();
|
||||
state.computeIfaceModSyms();
|
||||
state.computeIfaceVarSyms();
|
||||
state.computeScopeAliases();
|
||||
state.dump();
|
||||
LinkDotResolveVisitor visitorb(rootp,&state);
|
||||
}
|
||||
|
@ -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*) {
|
||||
|
140
src/V3Param.cpp
140
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<pair<AstIfaceRefDType*,AstIfaceRefDType*> > IfaceRefRefs; // Note may have duplicate entries
|
||||
|
||||
// STATE
|
||||
typedef std::map<AstVar*,AstVar*> VarCloneMap;
|
||||
typedef map<AstVar*,AstVar*> VarCloneMap;
|
||||
struct ModInfo {
|
||||
AstNodeModule* m_modp; // Module with specified name
|
||||
VarCloneMap m_cloneMap; // Map of old-varp -> new cloned varp
|
||||
ModInfo(AstNodeModule* modp) { m_modp=modp; }
|
||||
};
|
||||
typedef std::map<string,ModInfo> ModNameMap;
|
||||
typedef map<string,ModInfo> ModNameMap;
|
||||
ModNameMap m_modNameMap; // Hash of created module flavors by name
|
||||
|
||||
typedef std::map<string,string> LongMap;
|
||||
typedef map<string,string> LongMap;
|
||||
LongMap m_longMap; // Hash of very long names to unique identity number
|
||||
int m_longId;
|
||||
|
||||
typedef map<AstNode*,int> ValueMap;
|
||||
typedef map<int,int> NextValueMap;
|
||||
ValueMap m_valueMap; // Hash of node to param value
|
||||
NextValueMap m_nextValueMap;// Hash of param value to next value to be used
|
||||
|
||||
typedef multimap<int,AstNodeModule*> LevelModMap;
|
||||
LevelModMap m_todoModps; // Modules left to process
|
||||
|
||||
typedef deque<AstCell*> CellList;
|
||||
CellList m_cellps; // Cells left to process (in this module)
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
@ -91,7 +120,7 @@ private:
|
||||
// Pass 1, assign first letter to each gparam's name
|
||||
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
|
||||
if (AstVar* varp = stmtp->castVar()) {
|
||||
if (varp->isGParam()) {
|
||||
if (varp->isGParam()||varp->isIfaceRef()) {
|
||||
char ch = varp->name()[0];
|
||||
ch = toupper(ch); if (ch<'A' || ch>'Z') ch='Z';
|
||||
varp->user4(usedLetter[static_cast<int>(ch)]*256 + ch);
|
||||
@ -113,6 +142,27 @@ private:
|
||||
}
|
||||
return st;
|
||||
}
|
||||
string paramValueNumber(AstNode* nodep) {
|
||||
// Given a compilcated object create a number to use for param module assignment
|
||||
// Ideally would be relatively stable if design changes (not use pointer value),
|
||||
// and must return same value given same input node
|
||||
// Return must presently be numberic so doesn't collide with 'small' alphanumeric parameter names
|
||||
ValueMap::iterator it = m_valueMap.find(nodep);
|
||||
if (it != m_valueMap.end()) {
|
||||
return cvtToStr(it->second);
|
||||
} else {
|
||||
static int BUCKETS = 1000;
|
||||
V3Hash hash (nodep->name());
|
||||
int bucket = hash.hshval() % BUCKETS;
|
||||
int offset = 0;
|
||||
NextValueMap::iterator it = m_nextValueMap.find(bucket);
|
||||
if (it != m_nextValueMap.end()) { offset = it->second; it->second = offset + 1; }
|
||||
else { m_nextValueMap.insert(make_pair(bucket, offset + 1)); }
|
||||
int num = bucket + offset * BUCKETS;
|
||||
m_valueMap.insert(make_pair(nodep, num));
|
||||
return cvtToStr(num);
|
||||
}
|
||||
}
|
||||
void relinkPins(VarCloneMap* clonemapp, AstPin* startpinp) {
|
||||
for (AstPin* pinp = startpinp; pinp; pinp=pinp->nextp()->castPin()) {
|
||||
if (!pinp->modVarp()) pinp->v3fatalSrc("Not linked?\n");
|
||||
@ -123,9 +173,10 @@ private:
|
||||
pinp->modVarp(cloneiter->second);
|
||||
}
|
||||
}
|
||||
void visitCell(AstCell* nodep);
|
||||
void visitModules() {
|
||||
// Loop on all modules left to process
|
||||
// Hitting a cell adds to the appropriate leval of this level-sorted list,
|
||||
// Hitting a cell adds to the appropriate level of this level-sorted list,
|
||||
// so since cells originally exist top->bottom we process in top->bottom order too.
|
||||
while (!m_todoModps.empty()) {
|
||||
LevelModMap::iterator it = m_todoModps.begin();
|
||||
@ -134,7 +185,19 @@ private:
|
||||
if (!nodep->user5SetOnce()) { // Process once; note clone() must clear so we do it again
|
||||
UINFO(4," MOD "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this);
|
||||
// Note this may add to m_todoModps
|
||||
// Note above iterate may add to m_todoModps
|
||||
//
|
||||
// Process interface cells, then non-interface which may ref an interface cell
|
||||
for (int nonIf=0; nonIf<2; ++nonIf) {
|
||||
for (CellList::iterator it=m_cellps.begin(); it!=m_cellps.end(); ++it) {
|
||||
AstCell* nodep = *it;
|
||||
if ((nonIf==0 && nodep->modp()->castIface())
|
||||
|| (nonIf==1 && !nodep->modp()->castIface())) {
|
||||
visitCell(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_cellps.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -157,7 +220,10 @@ private:
|
||||
UINFO(4," MOD-dead? "<<nodep<<endl); // Should have been done by now, if not dead
|
||||
}
|
||||
}
|
||||
virtual void visit(AstCell* nodep, AstNUser*);
|
||||
virtual void visit(AstCell* nodep, AstNUser*) {
|
||||
// Must do ifaces first, so push to list and do in proper order
|
||||
m_cellps.push_back(nodep);
|
||||
}
|
||||
|
||||
// Make sure all parameters are constantified
|
||||
virtual void visit(AstVar* nodep, AstNUser*) {
|
||||
@ -218,7 +284,7 @@ private:
|
||||
//! Parameter subsitution for generated for loops.
|
||||
//! @todo Unlike generated IF, we don't have to worry about short-circuiting the conditional
|
||||
//! expression, since this is currently restricted to simple comparisons. If we ever do
|
||||
//! move to more generic constant expressions, such code will be neede here.
|
||||
//! move to more generic constant expressions, such code will be needed here.
|
||||
virtual void visit(AstBegin* nodep, AstNUser*) {
|
||||
if (nodep->genforp()) {
|
||||
AstGenFor* forp = nodep->genforp()->castGenFor();
|
||||
@ -317,11 +383,13 @@ public:
|
||||
//----------------------------------------------------------------------
|
||||
// VISITs
|
||||
|
||||
void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
void ParamVisitor::visitCell(AstCell* nodep) {
|
||||
// Cell: Check for parameters in the instantiation.
|
||||
nodep->iterateChildren(*this);
|
||||
if (!nodep->modp()) { nodep->dumpTree(cerr,"error:"); nodep->v3fatalSrc("Not linked?"); }
|
||||
if (nodep->paramsp()) {
|
||||
if (!nodep->modp()) nodep->v3fatalSrc("Not linked?");
|
||||
if (nodep->paramsp()
|
||||
|| 1 // Need to look for interfaces; could track when one exists, but should be harmless to always do this
|
||||
) {
|
||||
UINFO(4,"De-parameterize: "<<nodep<<endl);
|
||||
// Create new module name with _'s between the constants
|
||||
if (debug()>=10) nodep->dumpTree(cout,"-cell:\t");
|
||||
@ -359,6 +427,30 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
}
|
||||
}
|
||||
}
|
||||
IfaceRefRefs ifaceRefRefs;
|
||||
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
AstVar* modvarp = pinp->modVarp();
|
||||
if (modvarp->isIfaceRef()) {
|
||||
AstIfaceRefDType* portIrefp = modvarp->subDTypep()->castIfaceRefDType();
|
||||
//UINFO(9," portIfaceRef "<<portIrefp<<endl);
|
||||
if (!pinp->exprp()
|
||||
|| !pinp->exprp()->castVarRef()
|
||||
|| !pinp->exprp()->castVarRef()->varp()
|
||||
|| !pinp->exprp()->castVarRef()->varp()->subDTypep()
|
||||
|| !pinp->exprp()->castVarRef()->varp()->subDTypep()->castIfaceRefDType()) {
|
||||
pinp->v3error("Interface port '"<<modvarp->prettyName()<<"' is not connected to interface/modport pin expression");
|
||||
} else {
|
||||
AstIfaceRefDType* pinIrefp = pinp->exprp()->castVarRef()->varp()->subDTypep()->castIfaceRefDType();
|
||||
//UINFO(9," pinIfaceRef "<<pinIrefp<<endl);
|
||||
if (portIrefp->ifaceViaCellp() != pinIrefp->ifaceViaCellp()) {
|
||||
UINFO(9," IfaceRefDType needs reconnect "<<pinIrefp<<endl);
|
||||
longname += "_" + paramSmallName(nodep->modp(),pinp->modVarp())+paramValueNumber(pinIrefp);
|
||||
any_overrides = true;
|
||||
ifaceRefRefs.push_back(make_pair(portIrefp,pinIrefp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!any_overrides) {
|
||||
UINFO(8,"Cell parameters all match original values, skipping expansion.\n");
|
||||
@ -401,7 +493,7 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
// Note we allow multiple users of a parameterized model, thus we need to stash this info.
|
||||
for (AstNode* stmtp=modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
if (AstVar* varp = stmtp->castVar()) {
|
||||
if (varp->isIO() || varp->isGParam()) {
|
||||
if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) {
|
||||
// Cloning saved a pointer to the new node for us, so just follow that link.
|
||||
AstVar* oldvarp = varp->clonep()->castVar();
|
||||
//UINFO(8,"Clone list 0x"<<hex<<(uint32_t)oldvarp<<" -> 0x"<<(uint32_t)varp<<endl);
|
||||
@ -413,7 +505,21 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
// Relink parameter vars to the new module
|
||||
relinkPins(clonemapp, nodep->paramsp());
|
||||
|
||||
// Fix any interface references
|
||||
for (IfaceRefRefs::iterator it=ifaceRefRefs.begin(); it!=ifaceRefRefs.end(); ++it) {
|
||||
AstIfaceRefDType* portIrefp = it->first;
|
||||
AstIfaceRefDType* pinIrefp = it->second;
|
||||
AstIfaceRefDType* cloneIrefp = portIrefp->clonep()->castIfaceRefDType();
|
||||
UINFO(8," IfaceOld "<<portIrefp<<endl);
|
||||
UINFO(8," IfaceTo "<<pinIrefp<<endl);
|
||||
if (!cloneIrefp) portIrefp->v3fatalSrc("parameter clone didn't hit AstIfaceRefDType");
|
||||
UINFO(8," IfaceClo "<<cloneIrefp<<endl);
|
||||
cloneIrefp->ifacep(pinIrefp->ifaceViaCellp());
|
||||
UINFO(8," IfaceNew "<<cloneIrefp<<endl);
|
||||
}
|
||||
|
||||
// Assign parameters to the constants specified
|
||||
// DOES clone() so must be finished with module clonep() before here
|
||||
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) {
|
||||
AstVar* modvarp = pinp->modVarp();
|
||||
if (modvarp && pinp->exprp()) {
|
||||
@ -439,7 +545,7 @@ void ParamVisitor::visit(AstCell* nodep, AstNUser*) {
|
||||
} // if any_overrides
|
||||
|
||||
// Delete the parameters from the cell; they're not relevant any longer.
|
||||
nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
UINFO(8," Done with "<<nodep<<endl);
|
||||
//if (debug()>=10) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree"));
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ struct V3ParseBisonYYSType {
|
||||
AstCell* cellp;
|
||||
AstConst* constp;
|
||||
AstMemberDType* memberp;
|
||||
AstModportVarRef* modportvarrefp;
|
||||
AstNodeModule* modulep;
|
||||
AstNodeClassDType* classp;
|
||||
AstNodeDType* dtypep;
|
||||
|
@ -150,6 +150,14 @@ private:
|
||||
m_scopep->addActivep(clonep);
|
||||
clonep->iterateChildren(*this); // We iterate under the *clone*
|
||||
}
|
||||
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
|
||||
// Copy under the scope but don't recurse
|
||||
UINFO(4," Move "<<nodep<<endl);
|
||||
AstNode* clonep = nodep->cloneTree(false);
|
||||
nodep->user2p(clonep);
|
||||
m_scopep->addActivep(clonep);
|
||||
clonep->iterateChildren(*this); // We iterate under the *clone*
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
||||
// Add to list of blocks under this scope
|
||||
UINFO(4," Move "<<nodep<<endl);
|
||||
@ -215,12 +223,17 @@ private:
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
// VarRef needs to point to VarScope
|
||||
// Make sure variable has made user1p.
|
||||
nodep->varp()->accept(*this);
|
||||
AstVarScope* varscp = nodep->packagep()
|
||||
? (AstVarScope*)nodep->varp()->user3p()
|
||||
: (AstVarScope*)nodep->varp()->user1p();
|
||||
if (!varscp) nodep->v3fatalSrc("Can't locate varref scope");
|
||||
nodep->varScopep(varscp);
|
||||
if (!nodep->varp()) nodep->v3fatalSrc("Unlinked");
|
||||
if (nodep->varp()->isIfaceRef()) {
|
||||
nodep->varScopep(NULL);
|
||||
} else {
|
||||
nodep->varp()->accept(*this);
|
||||
AstVarScope* varscp = nodep->packagep()
|
||||
? (AstVarScope*)nodep->varp()->user3p()
|
||||
: (AstVarScope*)nodep->varp()->user1p();
|
||||
if (!varscp) nodep->v3fatalSrc("Can't locate varref scope");
|
||||
nodep->varScopep(varscp);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstScopeName* nodep, AstNUser*) {
|
||||
// If there's a %m in the display text, we add a special node that will contain the name()
|
||||
@ -298,6 +311,9 @@ private:
|
||||
virtual void visit(AstAssignAlias* nodep, AstNUser*) {
|
||||
movedDeleteOrIterate(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignVarScope* nodep, AstNUser*) {
|
||||
movedDeleteOrIterate(nodep);
|
||||
}
|
||||
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
||||
movedDeleteOrIterate(nodep);
|
||||
}
|
||||
|
@ -183,6 +183,18 @@ public:
|
||||
}
|
||||
return any;
|
||||
}
|
||||
void importFromIface(VSymGraph* graphp, const VSymEnt* srcp) {
|
||||
// Import interface tokens from source symbol table into this symbol table, recursively
|
||||
UINFO(9, " importIf se"<<(void*)this<<" from se"<<(void*)srcp<<endl);
|
||||
for (IdNameMap::const_iterator it=srcp->m_idNameMap.begin(); it!=srcp->m_idNameMap.end(); ++it) {
|
||||
const string& name = it->first;
|
||||
VSymEnt* srcp = it->second;
|
||||
VSymEnt* symp = new VSymEnt(graphp, srcp);
|
||||
reinsert(name, symp);
|
||||
// And recurse to create children
|
||||
srcp->importFromIface(graphp, symp);
|
||||
}
|
||||
}
|
||||
void cellErrorScopes(AstNode* lookp, string prettyName="") {
|
||||
if (prettyName=="") prettyName = lookp->prettyName();
|
||||
string scopes;
|
||||
|
@ -1073,6 +1073,14 @@ private:
|
||||
nodep->dtypeFrom(nodep->lhsp());
|
||||
}
|
||||
|
||||
virtual void visit(AstIfaceRefDType* nodep, AstNUser* vup) {
|
||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||
UINFO(5," IFACEREF "<<nodep<<endl);
|
||||
nodep->iterateChildren(*this, vup);
|
||||
nodep->dtypep(nodep);
|
||||
nodep->widthForce(1, 1); // Not really relevant
|
||||
UINFO(4,"dtWidthed "<<nodep<<endl);
|
||||
}
|
||||
virtual void visit(AstNodeClassDType* nodep, AstNUser* vup) {
|
||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||
UINFO(5," NODECLASS "<<nodep<<endl);
|
||||
|
@ -417,6 +417,7 @@ word [a-zA-Z0-9_]+
|
||||
"do" { FL; return yDO; }
|
||||
"endclocking" { FL; return yENDCLOCKING; }
|
||||
"endpackage" { FL; return yENDPACKAGE; }
|
||||
"endinterface" { FL; return yENDINTERFACE; }
|
||||
"endprogram" { FL; return yENDPROGRAM; }
|
||||
"endproperty" { FL; return yENDPROPERTY; }
|
||||
"enum" { FL; return yENUM; }
|
||||
@ -425,9 +426,11 @@ word [a-zA-Z0-9_]+
|
||||
"iff" { FL; return yIFF; }
|
||||
"import" { FL; return yIMPORT; }
|
||||
"inside" { FL; return yINSIDE; }
|
||||
"interface" { FL; return yINTERFACE; }
|
||||
"int" { FL; return yINT; }
|
||||
"logic" { FL; return yLOGIC; }
|
||||
"longint" { FL; return yLONGINT; }
|
||||
"modport" { FL; return yMODPORT; }
|
||||
"package" { FL; return yPACKAGE; }
|
||||
"packed" { FL; return yPACKED; }
|
||||
"priority" { FL; return yPRIORITY; }
|
||||
@ -460,7 +463,6 @@ word [a-zA-Z0-9_]+
|
||||
"dist" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"endclass" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"endgroup" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"endinterface" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"endsequence" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"expect" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"extends" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
@ -470,13 +472,11 @@ word [a-zA-Z0-9_]+
|
||||
"forkjoin" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"ignore_bins" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"illegal_bins" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"interface" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"intersect" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"join_any" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"join_none" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"local" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"matches" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"modport" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"new" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"null" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
"protected" { yyerrorf("Unsupported: SystemVerilog 2005 reserved word not implemented: %s",yytext); }
|
||||
|
121
src/verilog.y
121
src/verilog.y
@ -310,6 +310,7 @@ class AstSenTree;
|
||||
%token<fl> yENDCLOCKING "endclocking"
|
||||
%token<fl> yENDFUNCTION "endfunction"
|
||||
%token<fl> yENDGENERATE "endgenerate"
|
||||
%token<fl> yENDINTERFACE "endinterface"
|
||||
%token<fl> yENDMODULE "endmodule"
|
||||
%token<fl> yENDPACKAGE "endpackage"
|
||||
%token<fl> yENDPRIMITIVE "endprimitive"
|
||||
@ -337,9 +338,11 @@ class AstSenTree;
|
||||
%token<fl> yINSIDE "inside"
|
||||
%token<fl> yINT "int"
|
||||
%token<fl> yINTEGER "integer"
|
||||
%token<fl> yINTERFACE "interface"
|
||||
%token<fl> yLOCALPARAM "localparam"
|
||||
%token<fl> yLOGIC "logic"
|
||||
%token<fl> yLONGINT "longint"
|
||||
%token<fl> yMODPORT "modport"
|
||||
%token<fl> yMODULE "module"
|
||||
%token<fl> yNAND "nand"
|
||||
%token<fl> yNEGEDGE "negedge"
|
||||
@ -650,7 +653,7 @@ descriptionList: // IEEE: part of source_text
|
||||
description: // ==IEEE: description
|
||||
module_declaration { }
|
||||
// // udp_declaration moved into module_declaration
|
||||
//UNSUP interface_declaration { }
|
||||
| interface_declaration { }
|
||||
| program_declaration { }
|
||||
| package_declaration { }
|
||||
| package_item { if ($1) GRAMMARP->unitPackage($1->fileline())->addStmtp($1); }
|
||||
@ -844,14 +847,24 @@ port<nodep>: // ==IEEE: port
|
||||
// // IEEE: interface_port_header port_identifier { unpacked_dimension }
|
||||
// // Expanded interface_port_header
|
||||
// // We use instantCb here because the non-port form looks just like a module instantiation
|
||||
//UNSUP portDirNetE id/*interface*/ idAny/*port*/ rangeListE sigAttrListE
|
||||
//UNSUP { VARDTYPE($2); VARDONEA($<fl>3, $3, $4); PARSEP->instantCb($<fl>2, $2, $3, $4); PINNUMINC(); }
|
||||
//UNSUP portDirNetE yINTERFACE idAny/*port*/ rangeListE sigAttrListE
|
||||
//UNSUP { VARDTYPE($2); VARDONEA($<fl>3, $3, $4); PINNUMINC(); }
|
||||
//UNSUP portDirNetE id/*interface*/ '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE
|
||||
//UNSUP { VARDTYPE($2); VARDONEA($<fl>5, $5, $6); PARSEP->instantCb($<fl>2, $2, $5, $6); PINNUMINC(); }
|
||||
//UNSUP portDirNetE yINTERFACE '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE
|
||||
//UNSUP { VARDTYPE($2); VARDONEA($<fl>5, $5, $6); PINNUMINC(); }
|
||||
portDirNetE id/*interface*/ idAny/*port*/ variable_dimensionListE sigAttrListE
|
||||
{ $$ = new AstPort($<fl>2,PINNUMINC(),*$3);
|
||||
AstVar* varp=new AstVar($<fl>2,AstVarType(AstVarType::IFACEREF),*$3,VFlagChildDType(),
|
||||
new AstIfaceRefDType($<fl>2,"",*$2));
|
||||
if ($4) varp->v3error("Unsupported: Arrayed interfaces");
|
||||
varp->addAttrsp($5);
|
||||
$$->addNext(varp); }
|
||||
| portDirNetE yINTERFACE idAny/*port*/ rangeListE sigAttrListE
|
||||
{ $<fl>2->v3error("Unsupported: virtual interfaces"); }
|
||||
| portDirNetE id/*interface*/ '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE
|
||||
{ $$ = new AstPort($3,PINNUMINC(),*$5);
|
||||
AstVar* varp=new AstVar($<fl>2,AstVarType(AstVarType::IFACEREF),*$5,VFlagChildDType(),
|
||||
new AstIfaceRefDType($<fl>2,"",*$2,*$4));
|
||||
if ($6) varp->v3error("Unsupported: Arrayed interfaces");
|
||||
varp->addAttrsp($7);
|
||||
$$->addNext(varp); }
|
||||
| portDirNetE yINTERFACE '.' idAny/*modport*/ idAny/*port*/ rangeListE sigAttrListE
|
||||
{ $<fl>2->v3error("Unsupported: virtual interfaces"); }
|
||||
//
|
||||
// // IEEE: ansi_port_declaration, with [port_direction] removed
|
||||
// // IEEE: [ net_port_header | interface_port_header ] port_identifier { unpacked_dimension } [ '=' constant_expression ]
|
||||
@ -889,7 +902,7 @@ port<nodep>: // ==IEEE: port
|
||||
//UNSUP portDirNetE /*implicit*/ '.' portSig '(' portAssignExprE ')' sigAttrListE
|
||||
//UNSUP { UNSUP }
|
||||
//
|
||||
portDirNetE data_type portSig variable_dimensionListE sigAttrListE
|
||||
| portDirNetE data_type portSig variable_dimensionListE sigAttrListE
|
||||
{ $$=$3; VARDTYPE($2); $$->addNextNull(VARDONEP($$,$4,$5)); }
|
||||
| portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE
|
||||
{ $$=$4; VARDTYPE($3); $$->addNextNull(VARDONEP($$,$5,$6)); }
|
||||
@ -932,6 +945,54 @@ portSig<nodep>:
|
||||
//**********************************************************************
|
||||
// Interface headers
|
||||
|
||||
interface_declaration: // IEEE: interface_declaration + interface_nonansi_header + interface_ansi_header:
|
||||
// // timeunits_delcarationE is instead in interface_item
|
||||
intFront parameter_port_listE portsStarE ';'
|
||||
interface_itemListE yENDINTERFACE endLabelE
|
||||
{ if ($2) $1->addStmtp($2);
|
||||
if ($3) $1->addStmtp($3);
|
||||
if ($5) $1->addStmtp($5);
|
||||
SYMP->popScope($1); }
|
||||
//UNSUP yEXTERN intFront parameter_port_listE portsStarE ';' { }
|
||||
;
|
||||
|
||||
intFront<modulep>:
|
||||
yINTERFACE lifetimeE idAny/*new_interface*/
|
||||
{ $$ = new AstIface($1,*$3);
|
||||
$$->inLibrary(true);
|
||||
PARSEP->rootp()->addModulep($$);
|
||||
SYMP->pushNew($$); }
|
||||
;
|
||||
|
||||
interface_itemListE<nodep>:
|
||||
/* empty */ { $$ = NULL; }
|
||||
| interface_itemList { $$ = $1; }
|
||||
;
|
||||
|
||||
interface_itemList<nodep>:
|
||||
interface_item { $$ = $1; }
|
||||
| interface_itemList interface_item { $$ = $1->addNextNull($2); }
|
||||
;
|
||||
|
||||
interface_item<nodep>: // IEEE: interface_item + non_port_interface_item
|
||||
port_declaration ';' { $$ = $1; }
|
||||
// // IEEE: non_port_interface_item
|
||||
//UNSUP generate_region { $$ = $1; }
|
||||
| interface_or_generate_item { $$ = $1; }
|
||||
//UNSUP program_declaration { $$ = $1; }
|
||||
//UNSUP interface_declaration { $$ = $1; }
|
||||
| timeunits_declaration { $$ = $1; }
|
||||
// // See note in interface_or_generate item
|
||||
| module_common_item { $$ = $1; }
|
||||
;
|
||||
|
||||
interface_or_generate_item<nodep>: // ==IEEE: interface_or_generate_item
|
||||
// // module_common_item in interface_item, as otherwise duplicated
|
||||
// // with module_or_generate_item's module_common_item
|
||||
modport_declaration { $$ = $1; }
|
||||
//UNSUP extern_tf_declaration { $$ = $1; }
|
||||
;
|
||||
|
||||
//**********************************************************************
|
||||
// Program headers
|
||||
|
||||
@ -989,6 +1050,46 @@ program_generate_item<nodep>: // ==IEEE: program_generate_item
|
||||
//UNSUP elaboration_system_task { $$ = $1; }
|
||||
;
|
||||
|
||||
modport_declaration<nodep>: // ==IEEE: modport_declaration
|
||||
yMODPORT modport_itemList ';' { $$ = $2; }
|
||||
;
|
||||
|
||||
modport_itemList<nodep>: // IEEE: part of modport_declaration
|
||||
modport_item { $$ = $1; }
|
||||
| modport_itemList ',' modport_item { $$ = $1->addNextNull($3); }
|
||||
;
|
||||
|
||||
modport_item<nodep>: // ==IEEE: modport_item
|
||||
id/*new-modport*/ '(' modportPortsDeclList ')' { $$ = new AstModport($2,*$1,$3); }
|
||||
;
|
||||
|
||||
modportPortsDeclList<modportvarrefp>:
|
||||
modportPortsDecl { $$ = $1; }
|
||||
| modportPortsDeclList ',' modportPortsDecl { $$ = $1->addNextNull($3)->castModportVarRef(); }
|
||||
;
|
||||
|
||||
// IEEE: modport_ports_declaration + modport_simple_ports_declaration
|
||||
// + (modport_tf_ports_declaration+import_export) + modport_clocking_declaration
|
||||
// We've expanded the lists each take to instead just have standalone ID ports.
|
||||
// We track the type as with the V2k series of defines, then create as each ID is seen.
|
||||
modportPortsDecl<modportvarrefp>:
|
||||
// // IEEE: modport_simple_ports_declaration
|
||||
port_direction modportSimplePort { $$ = new AstModportVarRef($<fl>1,*$2,GRAMMARP->m_varIO); }
|
||||
// // IEEE: modport_clocking_declaration
|
||||
//UNSUP yCLOCKING idAny/*clocking_identifier*/ { }
|
||||
//UNSUP yIMPORT modport_tf_port { }
|
||||
//UNSUP yEXPORT modport_tf_port { }
|
||||
// Continuations of above after a comma.
|
||||
// // IEEE: modport_simple_ports_declaration
|
||||
| modportSimplePort { $$ = new AstModportVarRef($<fl>1,*$1,AstVarType::INOUT); }
|
||||
;
|
||||
|
||||
modportSimplePort<strp>: // IEEE: modport_simple_port or modport_tf_port, depending what keyword was earlier
|
||||
id { $$ = $1; }
|
||||
//UNSUP '.' idAny '(' ')' { }
|
||||
//UNSUP '.' idAny '(' expr ')' { }
|
||||
;
|
||||
|
||||
//************************************************
|
||||
// Variable Declarations
|
||||
|
||||
|
@ -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 (
|
||||
);
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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);
|
45
test_regress/t/t_interface1.v
Normal file
45
test_regress/t/t_interface1.v
Normal file
@ -0,0 +1,45 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by Wilson Snyder.
|
||||
|
||||
// Very simple test for interface pathclearing
|
||||
|
||||
interface ifc;
|
||||
logic [3:0] value;
|
||||
endinterface
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
integer cyc=1;
|
||||
|
||||
ifc itop();
|
||||
|
||||
sub c1 (.isub(itop),
|
||||
.i_value(4'h4));
|
||||
|
||||
always @ (posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc==20) begin
|
||||
if (c1.i_value != 4) $stop; // 'Normal' crossref just for comparison
|
||||
if (itop.value != 4) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module sub
|
||||
(
|
||||
ifc isub,
|
||||
input logic [3:0] i_value
|
||||
);
|
||||
|
||||
always @* begin
|
||||
isub.value = i_value;
|
||||
end
|
||||
endmodule : sub
|
18
test_regress/t/t_interface1_modport.pl
Executable file
18
test_regress/t/t_interface1_modport.pl
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
48
test_regress/t/t_interface1_modport.v
Normal file
48
test_regress/t/t_interface1_modport.v
Normal file
@ -0,0 +1,48 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by Wilson Snyder.
|
||||
|
||||
// Very simple test for interface pathclearing
|
||||
|
||||
interface ifc;
|
||||
integer hidden_from_isub;
|
||||
integer value;
|
||||
modport out_modport (output value);
|
||||
endinterface
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
integer cyc=1;
|
||||
|
||||
ifc itop();
|
||||
|
||||
sub c1 (.isub(itop),
|
||||
.i_value(4));
|
||||
|
||||
always @ (posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc==20) begin
|
||||
if (itop.value != 4) $stop;
|
||||
itop.hidden_from_isub = 20;
|
||||
if (itop.hidden_from_isub != 20) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module sub
|
||||
(
|
||||
ifc.out_modport isub,
|
||||
input integer i_value
|
||||
);
|
||||
|
||||
always @* begin
|
||||
isub.value = i_value;
|
||||
end
|
||||
endmodule
|
@ -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 (
|
||||
|
@ -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
|
||||
|
18
test_regress/t/t_interface_down.pl
Executable file
18
test_regress/t/t_interface_down.pl
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
72
test_regress/t/t_interface_down.v
Normal file
72
test_regress/t/t_interface_down.v
Normal file
@ -0,0 +1,72 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by Wilson Snyder.
|
||||
|
||||
interface ifc;
|
||||
integer value;
|
||||
endinterface
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
`ifdef INLINE_A //verilator inline_module
|
||||
`else //verilator no_inline_module
|
||||
`endif
|
||||
input clk;
|
||||
integer cyc=1;
|
||||
|
||||
ifc itop1a();
|
||||
ifc itop1b();
|
||||
ifc itop2a();
|
||||
ifc itop2b();
|
||||
|
||||
wrapper c1 (.isuba(itop1a),
|
||||
.isubb(itop1b),
|
||||
.i_valuea(14),
|
||||
.i_valueb(15));
|
||||
wrapper c2 (.isuba(itop2a),
|
||||
.isubb(itop2b),
|
||||
.i_valuea(24),
|
||||
.i_valueb(25));
|
||||
|
||||
always @ (posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc==20) begin
|
||||
if (itop1a.value != 14) $stop;
|
||||
if (itop1b.value != 15) $stop;
|
||||
if (itop2a.value != 24) $stop;
|
||||
if (itop2b.value != 25) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module wrapper
|
||||
(
|
||||
ifc isuba,
|
||||
ifc isubb,
|
||||
input integer i_valuea,
|
||||
input integer i_valueb
|
||||
);
|
||||
`ifdef INLINE_B //verilator inline_module
|
||||
`else //verilator no_inline_module
|
||||
`endif
|
||||
lower subsuba (.isub(isuba), .i_value(i_valuea));
|
||||
lower subsubb (.isub(isubb), .i_value(i_valueb));
|
||||
endmodule
|
||||
|
||||
module lower
|
||||
(
|
||||
ifc isub,
|
||||
input integer i_value
|
||||
);
|
||||
`ifdef INLINE_C //verilator inline_module
|
||||
`else //verilator no_inline_module
|
||||
`endif
|
||||
always @* begin
|
||||
isub.value = i_value;
|
||||
end
|
||||
endmodule
|
21
test_regress/t/t_interface_down_gen.pl
Executable file
21
test_regress/t/t_interface_down_gen.pl
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
$Self->{vlt} and $Self->unsupported("Verilator unsupported, interface generates changing types");
|
||||
$Self->{vcs} and $Self->unsupported("Commercially unsupported, interface crossrefs");
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
78
test_regress/t/t_interface_down_gen.v
Normal file
78
test_regress/t/t_interface_down_gen.v
Normal file
@ -0,0 +1,78 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by Wilson Snyder.
|
||||
|
||||
// This test demonstrates how not only parameters but the type of a parent
|
||||
// interface could propagate down to child modules, changing their data type
|
||||
// determinations. Note presently unsupported in all commercial simulators.
|
||||
|
||||
interface ifc;
|
||||
parameter MODE = 0;
|
||||
generate
|
||||
// Note block must be named per SystemVerilog 2005
|
||||
if (MODE==1) begin : g
|
||||
integer value;
|
||||
end
|
||||
else if (MODE==2) begin : g
|
||||
real value;
|
||||
end
|
||||
endgenerate
|
||||
endinterface
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
integer cyc=1;
|
||||
|
||||
ifc #(1) itop1a();
|
||||
ifc #(1) itop1b();
|
||||
ifc #(2) itop2a();
|
||||
ifc #(2) itop2b();
|
||||
|
||||
wrapper c1 (.isuba(itop1a),
|
||||
.isubb(itop1b),
|
||||
.i_valuea(14.1),
|
||||
.i_valueb(15.2));
|
||||
wrapper c2 (.isuba(itop2a),
|
||||
.isubb(itop2b),
|
||||
.i_valuea(24.3),
|
||||
.i_valueb(25.4));
|
||||
|
||||
always @ (posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc==20) begin
|
||||
if (itop1a.g.value != 14) $stop;
|
||||
if (itop1b.g.value != 15) $stop;
|
||||
if (itop2a.g.value != 24) $stop;
|
||||
if (itop2b.g.value != 25) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module wrapper
|
||||
(
|
||||
ifc isuba,
|
||||
ifc isubb,
|
||||
input real i_valuea,
|
||||
input real i_valueb
|
||||
);
|
||||
lower subsuba (.isub(isuba), .i_value(i_valuea));
|
||||
lower subsubb (.isub(isubb), .i_value(i_valueb));
|
||||
endmodule
|
||||
|
||||
module lower
|
||||
(
|
||||
ifc isub,
|
||||
input real i_value
|
||||
);
|
||||
always @* begin
|
||||
`error Commercial sims choke on cross ref here
|
||||
isub.g.value = i_value;
|
||||
end
|
||||
endmodule
|
22
test_regress/t/t_interface_down_inla.pl
Executable file
22
test_regress/t/t_interface_down_inla.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
top_filename("t/t_interface_down.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+INLINE_A'],
|
||||
verilator_flags2 => ['-trace'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
22
test_regress/t/t_interface_down_inlab.pl
Executable file
22
test_regress/t/t_interface_down_inlab.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
top_filename("t/t_interface_down.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+INLINE_A +define+INLINE_B'],
|
||||
verilator_flags2 => ['-trace'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
22
test_regress/t/t_interface_down_inlac.pl
Executable file
22
test_regress/t/t_interface_down_inlac.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
top_filename("t/t_interface_down.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+INLINE_A +define+INLINE_C'],
|
||||
verilator_flags2 => ['-trace'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
22
test_regress/t/t_interface_down_inlad.pl
Executable file
22
test_regress/t/t_interface_down_inlad.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
top_filename("t/t_interface_down.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+INLINE_A +define+INLINE_D'],
|
||||
verilator_flags2 => ['-trace'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
22
test_regress/t/t_interface_down_inlb.pl
Executable file
22
test_regress/t/t_interface_down_inlb.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
top_filename("t/t_interface_down.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+INLINE_B'],
|
||||
verilator_flags2 => ['-trace'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
22
test_regress/t/t_interface_down_inlbc.pl
Executable file
22
test_regress/t/t_interface_down_inlbc.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
top_filename("t/t_interface_down.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+INLINE_B +define+INLINE_C'],
|
||||
verilator_flags2 => ['-trace'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
22
test_regress/t/t_interface_down_inlbd.pl
Executable file
22
test_regress/t/t_interface_down_inlbd.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
top_filename("t/t_interface_down.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+INLINE_B +define+INLINE_D'],
|
||||
verilator_flags2 => ['-trace'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
22
test_regress/t/t_interface_down_inlc.pl
Executable file
22
test_regress/t/t_interface_down_inlc.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
top_filename("t/t_interface_down.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+INLINE_C'],
|
||||
verilator_flags2 => ['-trace'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
22
test_regress/t/t_interface_down_inlcd.pl
Executable file
22
test_regress/t/t_interface_down_inlcd.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
top_filename("t/t_interface_down.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+INLINE_C +define+INLINE_D'],
|
||||
verilator_flags2 => ['-trace'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
22
test_regress/t/t_interface_down_inld.pl
Executable file
22
test_regress/t/t_interface_down_inld.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
top_filename("t/t_interface_down.v");
|
||||
|
||||
compile (
|
||||
v_flags2 => ['+define+INLINE_D'],
|
||||
verilator_flags2 => ['-trace'],
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
18
test_regress/t/t_interface_gen.pl
Executable file
18
test_regress/t/t_interface_gen.pl
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
87
test_regress/t/t_interface_gen.v
Normal file
87
test_regress/t/t_interface_gen.v
Normal file
@ -0,0 +1,87 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by Wilson Snyder.
|
||||
|
||||
// Very simple test for interface pathclearing
|
||||
|
||||
`ifdef VCS
|
||||
`define UNSUPPORTED_MOD_IN_GENS
|
||||
`endif
|
||||
`ifdef VERILATOR
|
||||
`define UNSUPPORTED_MOD_IN_GENS
|
||||
`endif
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
integer cyc=1;
|
||||
|
||||
ifc #(1) itopa();
|
||||
ifc #(2) itopb();
|
||||
|
||||
sub #(1) ca (.isub(itopa),
|
||||
.i_value(4));
|
||||
sub #(2) cb (.isub(itopb),
|
||||
.i_value(5));
|
||||
|
||||
always @ (posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc==1) begin
|
||||
if (itopa.MODE != 1) $stop;
|
||||
if (itopb.MODE != 2) $stop;
|
||||
end
|
||||
if (cyc==20) begin
|
||||
if (itopa.get_value() != 4) $stop;
|
||||
if (itopb.get_value() != 5) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module sub
|
||||
#(parameter MODE = 0)
|
||||
(
|
||||
ifc.out_modport isub,
|
||||
input integer i_value
|
||||
);
|
||||
|
||||
`ifdef UNSUPPORTED_MOD_IN_GENS
|
||||
always @* isub.value = i_value;
|
||||
`else
|
||||
generate if (MODE == 1) begin
|
||||
always @* isub.valuea = i_value;
|
||||
end
|
||||
else if (MODE == 2) begin
|
||||
always @* isub.valueb = i_value;
|
||||
end
|
||||
endgenerate
|
||||
`endif
|
||||
|
||||
endmodule
|
||||
|
||||
interface ifc;
|
||||
parameter MODE = 0;
|
||||
// Modports under generates not supported by all commercial simulators
|
||||
`ifdef UNSUPPORTED_MOD_IN_GENS
|
||||
integer value;
|
||||
modport out_modport (output value);
|
||||
function integer get_value(); return value; endfunction
|
||||
`else
|
||||
generate if (MODE == 0) begin
|
||||
integer valuea;
|
||||
modport out_modport (output valuea);
|
||||
function integer get_valuea(); return valuea; endfunction
|
||||
end
|
||||
else begin
|
||||
integer valueb;
|
||||
modport out_modport (output valueb);
|
||||
function integer get_valueb(); return valueb; endfunction
|
||||
end
|
||||
endgenerate
|
||||
`endif
|
||||
endinterface
|
18
test_regress/t/t_interface_gen2.pl
Executable file
18
test_regress/t/t_interface_gen2.pl
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
70
test_regress/t/t_interface_gen2.v
Normal file
70
test_regress/t/t_interface_gen2.v
Normal file
@ -0,0 +1,70 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by Wilson Snyder.
|
||||
|
||||
// Very simple test for interface pathclearing
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
integer cyc=1;
|
||||
|
||||
ifc #(2) itopa();
|
||||
ifc #(4) itopb();
|
||||
|
||||
sub ca (.isub(itopa),
|
||||
.clk);
|
||||
sub cb (.isub(itopb),
|
||||
.clk);
|
||||
|
||||
always @ (posedge clk) begin
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("[%0t] cyc==%0d result=%b %b\n",$time, cyc, itopa.valueo, itopb.valueo);
|
||||
`endif
|
||||
cyc <= cyc + 1;
|
||||
itopa.valuei <= cyc[1:0];
|
||||
itopb.valuei <= cyc[3:0];
|
||||
if (cyc==1) begin
|
||||
if (itopa.WIDTH != 2) $stop;
|
||||
if (itopb.WIDTH != 4) $stop;
|
||||
if ($bits(itopa.valueo) != 2) $stop;
|
||||
if ($bits(itopb.valueo) != 4) $stop;
|
||||
if ($bits(itopa.out_modport.valueo) != 2) $stop;
|
||||
if ($bits(itopb.out_modport.valueo) != 4) $stop;
|
||||
end
|
||||
if (cyc==4) begin
|
||||
if (itopa.valueo != 2'b11) $stop;
|
||||
if (itopb.valueo != 4'b0011) $stop;
|
||||
end
|
||||
if (cyc==5) begin
|
||||
if (itopa.valueo != 2'b00) $stop;
|
||||
if (itopb.valueo != 4'b0100) $stop;
|
||||
end
|
||||
if (cyc==20) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
interface ifc
|
||||
#(parameter WIDTH = 1);
|
||||
// verilator lint_off MULTIDRIVEN
|
||||
logic [WIDTH-1:0] valuei;
|
||||
logic [WIDTH-1:0] valueo;
|
||||
// verilator lint_on MULTIDRIVEN
|
||||
modport out_modport (input valuei, output valueo);
|
||||
endinterface
|
||||
|
||||
// Note not parameterized
|
||||
module sub
|
||||
(
|
||||
ifc.out_modport isub,
|
||||
input clk
|
||||
);
|
||||
always @(posedge clk) isub.valueo <= isub.valuei + 1;
|
||||
endmodule
|
18
test_regress/t/t_interface_gen3.pl
Executable file
18
test_regress/t/t_interface_gen3.pl
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
70
test_regress/t/t_interface_gen3.v
Normal file
70
test_regress/t/t_interface_gen3.v
Normal file
@ -0,0 +1,70 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by Wilson Snyder.
|
||||
|
||||
// Very simple test for interface pathclearing
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
integer cyc=1;
|
||||
|
||||
ifc #(2) itopa();
|
||||
ifc #(4) itopb();
|
||||
|
||||
sub ca (.isub(itopa.out_modport),
|
||||
.clk);
|
||||
sub cb (.isub(itopb.out_modport),
|
||||
.clk);
|
||||
|
||||
always @ (posedge clk) begin
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("[%0t] cyc==%0d result=%b %b\n",$time, cyc, itopa.valueo, itopb.valueo);
|
||||
`endif
|
||||
cyc <= cyc + 1;
|
||||
itopa.valuei <= cyc[1:0];
|
||||
itopb.valuei <= cyc[3:0];
|
||||
if (cyc==1) begin
|
||||
if (itopa.WIDTH != 2) $stop;
|
||||
if (itopb.WIDTH != 4) $stop;
|
||||
if ($bits(itopa.valueo) != 2) $stop;
|
||||
if ($bits(itopb.valueo) != 4) $stop;
|
||||
if ($bits(itopa.out_modport.valueo) != 2) $stop;
|
||||
if ($bits(itopb.out_modport.valueo) != 4) $stop;
|
||||
end
|
||||
if (cyc==4) begin
|
||||
if (itopa.valueo != 2'b11) $stop;
|
||||
if (itopb.valueo != 4'b0011) $stop;
|
||||
end
|
||||
if (cyc==5) begin
|
||||
if (itopa.valueo != 2'b00) $stop;
|
||||
if (itopb.valueo != 4'b0100) $stop;
|
||||
end
|
||||
if (cyc==20) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
interface ifc
|
||||
#(parameter WIDTH = 1);
|
||||
// verilator lint_off MULTIDRIVEN
|
||||
logic [WIDTH-1:0] valuei;
|
||||
logic [WIDTH-1:0] valueo;
|
||||
// verilator lint_on MULTIDRIVEN
|
||||
modport out_modport (input valuei, output valueo);
|
||||
endinterface
|
||||
|
||||
// Note not parameterized
|
||||
module sub
|
||||
(
|
||||
ifc.out_modport isub,
|
||||
input clk
|
||||
);
|
||||
always @(posedge clk) isub.valueo <= isub.valuei + 1;
|
||||
endmodule
|
@ -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 (
|
||||
|
22
test_regress/t/t_interface_mismodport_bad.pl
Executable file
22
test_regress/t/t_interface_mismodport_bad.pl
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
compile (
|
||||
verilator_flags2 => ["--lint-only"],
|
||||
verilator_make_gcc => 0,
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
fails => 1,
|
||||
expect=>
|
||||
'%Error: t/t_interface_mismodport_bad.v:\d+: Can\'t find definition of \'bad\' in dotted signal: isub.bad
|
||||
.*%Error: Exiting due to.*',
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
37
test_regress/t/t_interface_mismodport_bad.v
Normal file
37
test_regress/t/t_interface_mismodport_bad.v
Normal file
@ -0,0 +1,37 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2013 by Wilson Snyder.
|
||||
|
||||
interface ifc;
|
||||
integer ok;
|
||||
integer bad;
|
||||
modport out_modport (output ok);
|
||||
endinterface
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
integer cyc=1;
|
||||
|
||||
ifc itop();
|
||||
|
||||
counter_ansi c1 (.isub(itop),
|
||||
.i_value(4'h4));
|
||||
|
||||
endmodule
|
||||
|
||||
module counter_ansi
|
||||
(
|
||||
ifc.out_modport isub,
|
||||
input logic [3:0] i_value
|
||||
);
|
||||
|
||||
always @* begin
|
||||
isub.ok = i_value;
|
||||
isub.bad = i_value; // Illegal access
|
||||
end
|
||||
endmodule
|
@ -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 (
|
||||
|
@ -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
|
||||
|
@ -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 (
|
||||
|
21
test_regress/t/t_interface_top_bad.pl
Executable file
21
test_regress/t/t_interface_top_bad.pl
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 by Wilson Snyder. This program is free software; you can
|
||||
# redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
|
||||
compile (
|
||||
verilator_flags2 => ["--lint-only"],
|
||||
verilator_make_gcc => 0,
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
fails => 1,
|
||||
expect=>
|
||||
'%Error: t/t_interface_top_bad.v:\d+: Unsupported: Interfaced port on top level module',
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
@ -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;
|
@ -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);
|
||||
|
@ -8,7 +8,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
||||
# Version 2.0.
|
||||
|
||||
compile (
|
||||
v_flags => [],
|
||||
);
|
||||
|
||||
execute (
|
||||
|
Loading…
Reference in New Issue
Block a user