From 17bf13fcb66ace98783d18bdd2c08a10546a9c6f Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 5 Dec 2009 10:38:49 -0500 Subject: [PATCH] Support DPI context imports --- include/verilated.cpp | 63 +++++------- include/verilated.h | 43 +++++++- include/verilateddpi.cpp | 43 +++++--- include/verilatedimp.h | 143 ++++++++++++++++++++++++++ include/verilatedos.h | 2 + src/V3Ast.h | 12 ++- src/V3AstNodes.cpp | 52 +++++++++- src/V3AstNodes.h | 17 ++- src/V3Begin.cpp | 2 +- src/V3Combine.cpp | 6 +- src/V3EmitC.cpp | 8 +- src/V3EmitCSyms.cpp | 23 ++++- src/V3Gate.cpp | 2 +- src/V3Inline.cpp | 2 +- src/V3LinkResolve.cpp | 6 ++ src/V3Scope.cpp | 16 ++- src/V3Signed.cpp | 3 +- src/V3Task.cpp | 49 +++++++-- src/V3Width.cpp | 4 +- test_regress/t/t_dpi_context.pl | 19 ++++ test_regress/t/t_dpi_context.v | 52 ++++++++++ test_regress/t/t_dpi_context_c.cpp | 104 +++++++++++++++++++ test_regress/t/t_dpi_context_noopt.pl | 22 ++++ test_regress/t/t_dpi_import.v | 9 -- test_regress/t/t_dpi_import_c.cpp | 12 --- 25 files changed, 596 insertions(+), 118 deletions(-) create mode 100644 include/verilatedimp.h create mode 100755 test_regress/t/t_dpi_context.pl create mode 100644 test_regress/t/t_dpi_context.v create mode 100644 test_regress/t/t_dpi_context_c.cpp create mode 100755 test_regress/t/t_dpi_context_noopt.pl diff --git a/include/verilated.cpp b/include/verilated.cpp index 8da6b0b16..8b4a2e28a 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -23,7 +23,8 @@ /// //========================================================================= -#include "verilated.h" +#define _VERILATED_CPP_ +#include "verilatedimp.h" #include #include @@ -37,38 +38,11 @@ int Verilated::s_debug = 1; bool Verilated::s_calcUnusedSigs = false; bool Verilated::s_gotFinish = false; bool Verilated::s_assertOn = true; +VL_THREAD const VerilatedScope* Verilated::t_dpiScopep = NULL; +VL_THREAD const char* Verilated::t_dpiFilename = ""; +VL_THREAD int Verilated::t_dpiLineno = 0; -//=========================================================================== -// Local Implementation Globals -// (Not in Verilated as they can be slow, and we want to mimimize the STL imports) - -class VerilatedImp { -protected: - friend class Verilated; - typedef vector ArgVec; - static ArgVec s_argVec; // Argument list - static bool s_argVecLoaded; // Ever loaded argument list - // METHODS -public: // But only for this C file - static string argPlusMatch(const char* prefixp) { - int len = strlen(prefixp); - if (!s_argVecLoaded) { - s_argVecLoaded = true; // Complain only once - vl_fatal("unknown",0,"", - "%Error: Verilog called $test$plusargs or $value$plusargs without" - " testbench C first calling Verilated::commandArgs(argc,argv)."); - } - for (ArgVec::iterator it=s_argVec.begin(); it!=s_argVec.end(); ++it) { - if ((*it)[0]=='+') { - if (0==strncmp(prefixp, it->c_str()+1, len)) return *it; - } - } - return ""; - } -}; - -VerilatedImp::ArgVec VerilatedImp::s_argVec; -bool VerilatedImp::s_argVecLoaded = false; +VerilatedImp VerilatedImp::s_s; //=========================================================================== // User definable functions @@ -938,12 +912,7 @@ const char* Verilated::catName(const char* n1, const char* n2) { } void Verilated::commandArgs(int argc, const char** argv) { - VerilatedImp::s_argVec.clear(); - for (int i=0; iname(); } svScope svGetScopeFromName(const char* scopeName) { - _VL_SVDPI_UNIMP(); return NULL; + return (svScope)VerilatedImp::scopeFind(scopeName); } int svPutUserData(const svScope scope, void *userKey, void* userData) { - _VL_SVDPI_UNIMP(); - return -1; // -1 == error + VerilatedImp::userInsert(scope,userKey,userData); + return 0; } void* svGetUserData(const svScope scope, void* userKey) { - return NULL; // NULL == error + return VerilatedImp::userFind(scope,userKey); } int svGetCallerInfo(const char** fileNamepp, int *lineNumberp) { - _VL_SVDPI_UNIMP(); return false; - //UNSUP if (!Verilated::dpiInContext) { _VL_SVDPI_CONTEXT_WARN(); return false; } - //UNSUP if (fileNamep) *fileNamepp = Verilated::dpiFilenamep; // thread local - //UNSUP if (lineNumberp) *lineNumberp = Verilated::dpiLineno; // thread local - //UNSUP return true; + if (VL_UNLIKELY(!Verilated::dpiInContext())) { _VL_SVDPI_CONTEXT_WARN(); return false; } + if (VL_LIKELY(fileNamepp)) *fileNamepp = Verilated::dpiFilenamep(); // thread local + if (VL_LIKELY(lineNumberp)) *lineNumberp = Verilated::dpiLineno(); // thread local + return true; } //====================================================================== diff --git a/include/verilatedimp.h b/include/verilatedimp.h new file mode 100644 index 000000000..f647e722a --- /dev/null +++ b/include/verilatedimp.h @@ -0,0 +1,143 @@ +// -*- C++ -*- +//************************************************************************* +// +// Copyright 2009-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. +// +// Verilator is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +//========================================================================= +/// +/// \file +/// \brief Verilator: Implementation Header, only for verilated.cpp internals. +/// +/// Code available from: http://www.veripool.org/verilator +/// +//========================================================================= + + +#ifndef _VERILATEDIMP_H_ +#define _VERILATEDIMP_H_ 1 ///< Header Guard + +#if !defined(_VERILATED_CPP_) && !defined(_VERILATEDDPI_CPP_) +# error "verilatedimp.h only to be included by verilated*.cpp internals" +#endif + +#include "verilatedos.h" +#include "verilated.h" + +#include +#include + +class VerilatedScope; + +//====================================================================== +// Types + +struct VerilatedCStrCmp { + // For ordering maps keyed by const char*'s + bool operator() (const char *a, const char *b) { + return std::strcmp(a, b) < 0; + } +}; + +class VerilatedImp { + // Whole class is internal use only - Global information shared between verilated*.cpp files. + + // TYPES + typedef vector ArgVec; + typedef map,void*> UserMap; + typedef map ScopeNameMap; + + // MEMBERS + static VerilatedImp s_s; ///< Static Singleton; One and only static this + + ArgVec m_argVec; // Argument list + bool m_argVecLoaded; // Ever loaded argument list + UserMap m_userMap; ///< Map of <(scope,userkey), userData> + ScopeNameMap m_nameMap; ///< Map of + +public: // But only for verilated*.cpp + // CONSTRUCTORS + VerilatedImp() : m_argVecLoaded(false) {} + ~VerilatedImp() {} + + // METHODS - arguments + static void commandArgs(int argc, const char** argv) { + s_s.m_argVec.clear(); + for (int i=0; ic_str()+1, len)) return *it; + } + } + return ""; + } + + // METHODS - user scope tracking + // We implement this as a single large map instead of one map per scope + // There's often many more scopes than userdata's and thus having a ~48byte + // per map overhead * N scopes would take much more space and cache thrashing. + static inline void userInsert(const void* scopep, void* userKey, void* userData) { + UserMap::iterator it=s_s.m_userMap.find(make_pair(scopep,userKey)); + if (it != s_s.m_userMap.end()) it->second = userData; + // When we support VL_THREADs, we need a lock around this insert, as it's runtime + else s_s.m_userMap.insert(it, make_pair(make_pair(scopep,userKey),userData)); + } + static inline void* userFind(const void* scopep, void* userKey) { + UserMap::iterator it=s_s.m_userMap.find(make_pair(scopep,userKey)); + if (VL_LIKELY(it != s_s.m_userMap.end())) return it->second; + else return NULL; + } +private: + /// Symbol table destruction cleans up the entries for each scope. + static void userEraseScope(const VerilatedScope* scopep) { + // Slow ok - called once/scope on destruction, so we simply iterate. + for (UserMap::iterator it=s_s.m_userMap.begin(); it!=s_s.m_userMap.end(); ) { + if (it->first.first == scopep) { + s_s.m_userMap.erase(it++); + } else { + ++it; + } + } + } + +public: // But only for verilated*.cpp + // METHODS - scope name + static void scopeInsert(const VerilatedScope* scopep) { + // Slow ok - called once/scope at construction + ScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name()); + if (it == s_s.m_nameMap.end()) { + s_s.m_nameMap.insert(it, make_pair(scopep->name(),scopep)); + } + } + static inline const VerilatedScope* scopeFind(const char* namep) { + ScopeNameMap::iterator it=s_s.m_nameMap.find(namep); + if (it != s_s.m_nameMap.end()) return it->second; + else return NULL; + } + static void scopeErase(const VerilatedScope* scopep) { + // Slow ok - called once/scope at destruction + userEraseScope(scopep); + ScopeNameMap::iterator it=s_s.m_nameMap.find(scopep->name()); + if (it != s_s.m_nameMap.end()) s_s.m_nameMap.erase(it); + } + +}; + +#endif // Guard diff --git a/include/verilatedos.h b/include/verilatedos.h index 888419d4f..dae61ce6f 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -38,6 +38,7 @@ # define VL_ATTR_ALIGNED(alignment) __attribute__ ((aligned (alignment))) # define VL_ATTR_NORETURN __attribute__ ((noreturn)) # define VL_ATTR_UNUSED __attribute__ ((unused)) +# define VL_FUNC __func__ # define VL_LIKELY(x) __builtin_expect(!!(x), 1) # define VL_UNLIKELY(x) __builtin_expect(!!(x), 0) # define VL_PREFETCH_RD(p) __builtin_prefetch((p),0) @@ -47,6 +48,7 @@ # define VL_ATTR_ALIGNED(alignment) ///< Align structure to specified byte alignment # define VL_ATTR_NORETURN ///< Function does not ever return # define VL_ATTR_UNUSED ///< Function that may be never used +# define VL_FUNC "__func__" ///< Name of current function for error macros # define VL_LIKELY(x) (!!(x)) ///< Boolean expression more often true than false # define VL_UNLIKELY(x) (!!(x)) ///< Boolean expression more often false than true # define VL_PREFETCH_RD(p) ///< Prefetch data with read intent diff --git a/src/V3Ast.h b/src/V3Ast.h index 23d55be7a..c1552f6fa 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -208,7 +208,9 @@ public: REAL, REALTIME, SHORTINT, SHORTREAL, TIME, // Closer to a class type, but limited usage STRING, - // Internal types + // Internal types for mid-steps + SCOPEPTR, CHARPTR, + // Internal types, eliminated after parsing LOGIC_IMPLICIT }; enum en m_e; @@ -217,6 +219,7 @@ public: "bit", "byte", "chandle", "int", "integer", "logic", "longint", "real", "realtime", "shortint", "shortreal", "time", "string", + "VerilatedScope*", "char*", "LOGIC_IMPLICIT" }; return names[m_e]; @@ -225,6 +228,7 @@ public: static const char* names[] = { "unsigned char", "char", "void*", "int", "int", "svLogic", "long long", "double", "double", "short int", "float", "long long", + "dpiScope", "const char*", "char*", "" }; @@ -1213,10 +1217,11 @@ public: m_text = textp; // Copy it } ASTNODE_BASE_FUNCS(NodeText) - const string& text() const { return m_text; } + virtual void dump(ostream& str=cout); virtual V3Hash sameHash() const { return V3Hash(text()); } virtual bool same(AstNode* samep) const { return text()==samep->castNodeText()->text(); } + const string& text() const { return m_text; } }; struct AstNodeDType : public AstNode { @@ -1338,6 +1343,9 @@ public: // op2 = Pin interconnection list AstNode* pinsp() const { return op2p()->castNode(); } void addPinsp(AstNode* nodep) { addOp2p(nodep); } + // op3 = scope tracking + AstScopeName* scopeNamep() const { return op3p()->castScopeName(); } + void scopeNamep(AstNode* nodep) { setNOp3p(nodep); } }; struct AstNodeModule : public AstNode { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index db7e5531c..8e06977b0 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -107,7 +107,12 @@ string AstVar::vlArgType(bool named, bool forReturn) const { if (forReturn) v3fatalSrc("verilator internal data is never passed as return, but as first argument"); string arg; if (isWide() && isInOnly()) arg += "const "; - if (widthMin() <= 8) { + AstBasicDType* bdtypep = basicp(); + if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) { + arg += "const char*"; + } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) { + arg += "const VerilatedScope*"; + } else if (widthMin() <= 8) { arg += "CData"; } else if (widthMin() <= 16) { arg += "SData"; @@ -296,12 +301,38 @@ void AstScope::cloneRelink() { } string AstScope::nameDotless() const { - string dotless = shortName(); + string out = shortName(); string::size_type pos; - while ((pos=dotless.find(".")) != string::npos) { - dotless.replace(pos, 1, "__"); + while ((pos=out.find(".")) != string::npos) { + out.replace(pos, 1, "__"); } - return dotless; + return out; +} + +string AstScopeName::scopePrettyName() const { + string out; + for (AstText* textp=scopeAttrp(); textp; textp=textp->nextp()->castText()) { + out += textp->text(); + } + // TOP will be replaced by top->name() + if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,""); + return AstNode::prettyName(out); +} + +string AstScopeName::scopeSymName() const { + string out; + for (AstText* textp=scopeAttrp(); textp; textp=textp->nextp()->castText()) { + out += textp->text(); + } + if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,""); + string::size_type pos; + while ((pos=out.find(".")) != string::npos) { + out.replace(pos, 1, "__"); + } + while ((pos=out.find("__DOT__")) != string::npos) { + out.replace(pos, 7, "__"); + } + return out; } bool AstSenTree::hasClocked() { @@ -560,6 +591,17 @@ void AstTraceInc::dump(ostream& str) { if (declp()) { declp()->dump(str); } else { str<<"%Error:UNLINKED"; } } +void AstNodeText::dump(ostream& str) { + this->AstNode::dump(str); + string out = text(); + string::size_type pos; + if ((pos = out.find("\n")) != string::npos) { + out.erase(pos,out.length()-pos); + out += "..."; + } + str<<" \""<AstNode::dump(str); if (source()) str<<" [SRC]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index e00237857..980775f2d 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -612,6 +612,7 @@ struct AstScope : public AstNode { // Parents: MODULE // Children: NODEBLOCK private: + // An AstScope->name() is special: . indicates an uninlined scope, __DOT__ an inlined scope string m_name; // Name AstScope* m_aboveScopep; // Scope above this one in the hierarchy (NULL if top) AstCell* m_aboveCellp; // Cell above this in the hierarchy (NULL if top) @@ -2001,17 +2002,22 @@ public: virtual void dump(ostream& str=cout); }; -struct AstScopeName : public AstNode { - // For display %m +struct AstScopeName : public AstNodeMath { + // For display %m and DPI context imports // Parents: DISPLAY // Children: TEXT - AstScopeName(FileLine* fl) - : AstNode(fl) {} + AstScopeName(FileLine* fl) : AstNodeMath(fl) { + width(64,64); } ASTNODE_NODE_FUNCS(ScopeName, SCOPENAME) virtual V3Hash sameHash() const { return V3Hash(); } virtual bool same(AstNode* samep) const { return true; } + virtual string emitVerilog() { return ""; } + virtual string emitC() { V3ERROR_NA; return ""; } + virtual bool cleanOut() { return true; } AstText* scopeAttrp() const { return op1p()->castText(); } void scopeAttrp(AstNode* nodep) { addOp1p(nodep); } + string scopeSymName() const; // Name for __Vscope variable including children + string scopePrettyName() const; // Name for __Vscope printing }; struct AstUdpTable : public AstNode { @@ -3177,9 +3183,12 @@ public: addNOp1p(argsp); } AstCCall(AstCCall* oldp, AstCFunc* funcp) // Replacement form for V3Combine + // Note this removes old attachments from the oldp : AstNodeStmt(oldp->fileline()) { m_funcp = funcp; m_hiername = oldp->hiername(); + m_argTypes = oldp->argTypes(); + if (oldp->argsp()) addNOp1p(oldp->argsp()->unlinkFrBackWithNext()); } ASTNODE_NODE_FUNCS(CCall, CCALL) virtual void dump(ostream& str=cout); diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index db84ee3e2..44f3611be 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -189,7 +189,7 @@ private: // To keep correct visual order, must add before other Text's AstNode* afterp = nodep->scopeAttrp(); if (afterp) afterp->unlinkFrBackWithNext(); - nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"."+AstNode::prettyName(m_namedScope))); + nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_namedScope)); if (afterp) nodep->scopeAttrp(afterp); } nodep->iterateChildren(*this); diff --git a/src/V3Combine.cpp b/src/V3Combine.cpp index 7504befc0..e11d1509e 100644 --- a/src/V3Combine.cpp +++ b/src/V3Combine.cpp @@ -72,6 +72,10 @@ protected: //***** optimization levels bool emptyFunctionDeletion() { return true; } bool duplicateFunctionCombine() { return true; } + // Note this is disabled, it still needed work + // Also repair it for DPI functions; when make __common need to insure proper + // flags get inherited from the old to new AstCFunc, and that AstText doesn't + // get split between functions causing the text to have a danginling reference. bool statementCombine() { return false && duplicateFunctionCombine(); } }; @@ -101,7 +105,7 @@ public: if (callp->funcp() != oldfuncp) callp->v3fatalSrc("Call list broken, points to call w/different func"); if (newfuncp) { AstCCall* newp = new AstCCall(callp, newfuncp); - newp->argTypes(callp->argTypes()); + // Special new AstCCall form above transfers children of callp to newfuncp callp->replaceWith(newp); addCall(newp); // Fix the table } else { // Just deleting empty function diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index c77fbb591..34297915a 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -223,6 +223,10 @@ public: if (nodep->addNewline()) text += "\n"; displayNode(nodep, text, nodep->exprsp(), false); } + virtual void visit(AstScopeName* nodep, AstNUser*) { + // For use under AstCCalls for dpiImports. ScopeNames under displays are handled in AstDisplay + putbs("(&(vlSymsp->__Vscope_"+nodep->scopeSymName()+"))"); + } virtual void visit(AstSFormat* nodep, AstNUser*) { displayNode(nodep, nodep->text(), nodep->exprsp(), false); } @@ -1198,9 +1202,7 @@ void EmitCStmts::displayNode(AstNode* nodep, const string& vformat, AstNode* exp if (!nodep->castNodeDisplay()) nodep->v3fatalSrc("Non-Display with %m"); AstScopeName* scopenamep = nodep->castNodeDisplay()->scopeNamep(); if (!scopenamep) nodep->v3fatalSrc("Display with %m but no AstScopeName"); - for (AstText* textp=scopenamep->scopeAttrp(); textp; textp=textp->nextp()->castText()) { - emitDispState.pushFormat(textp->text()); - } + emitDispState.pushFormat(scopenamep->scopePrettyName()); break; } case 'u': diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 7cc83a497..0880adedc 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -44,10 +44,13 @@ class EmitCSyms : EmitCBaseVisitor { // AstNodeModule::user1() -> bool. Set true __Vconfigure called AstUser1InUse m_inuser1; + typedef map ScopeNames; + // STATE AstNodeModule* m_modp; // Current module typedef pair ScopeModPair; vector m_scopes; // Every scope by module + ScopeNames m_scopeNames; // Each unique AstScopeName V3LanguageWords m_words; // Reserved word detector int m_coverBins; // Coverage bin number @@ -94,6 +97,12 @@ class EmitCSyms : EmitCBaseVisitor { nameCheck(nodep); m_scopes.push_back(make_pair(nodep, m_modp)); } + virtual void visit(AstScopeName* nodep, AstNUser*) { + string name = nodep->scopeSymName(); + if (m_scopeNames.find(name) == m_scopeNames.end()) { + m_scopeNames.insert(make_pair(name, nodep)); + } + } virtual void visit(AstCoverDecl* nodep, AstNUser*) { // Assign numbers to all bins, so we know how big of an array to use if (!nodep->dataDeclNullp()) { // else duplicate we don't need code for @@ -101,7 +110,6 @@ class EmitCSyms : EmitCBaseVisitor { } } // NOPs - virtual void visit(AstNodeStmt*, AstNUser*) {} virtual void visit(AstConst*, AstNUser*) {} // Default virtual void visit(AstNode* nodep, AstNUser*) { @@ -174,6 +182,11 @@ void EmitCSyms::emitInt() { puts("uint32_t\t__Vcoverage["); puts(cvtToStr(m_coverBins)); puts("];\n"); } + puts("\n// SCOPE NAMES\n"); + for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { + puts("VerilatedScope __Vscope_"+it->second->scopeSymName()+";\n"); + } + puts("\n// CREATORS\n"); puts(symClassName()+"("+topClassName()+"* topp, const char* namep);\n"); puts((string)"~"+symClassName()+"() {};\n"); @@ -242,6 +255,7 @@ void EmitCSyms::emitImp() { puts(scopep->nameDotless()+";\n"); } } + puts("// Setup each module's pointer back to symbol table (for public functions)\n"); puts("TOPp->__Vconfigure(this, true);\n"); for (vector::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { @@ -256,6 +270,13 @@ void EmitCSyms::emitImp() { } } + puts("// Setup scope names\n"); + for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { + puts("__Vscope_"+it->second->scopeSymName()+".configure(name(),"); + putsQuoted(it->second->scopePrettyName()); + puts(");\n"); + } + puts("}\n"); puts("\n"); } diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index bbfeb1da6..f9432f2e2 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -110,7 +110,7 @@ public: : GateEitherVertex(graphp,scopep), m_nodep(nodep), m_activep(activep), m_slow(slow) {} virtual ~GateLogicVertex() {} // Accessors - virtual string name() const { return (cvtToStr((void*)m_nodep)+"@"+scopep()->name()); } + virtual string name() const { return (cvtToStr((void*)m_nodep)+"@"+scopep()->prettyName()); } virtual string dotColor() const { return "yellow"; } AstNode* nodep() const { return m_nodep; } AstActive* activep() const { return m_activep; } diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 265dafe22..e228c9a17 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -266,7 +266,7 @@ private: // To keep correct visual order, must add before other Text's AstNode* afterp = nodep->scopeAttrp(); if (afterp) afterp->unlinkFrBackWithNext(); - nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"."+m_cellp->prettyName())); + nodep->scopeAttrp(new AstText(nodep->fileline(), (string)"__DOT__"+m_cellp->name())); if (afterp) nodep->scopeAttrp(afterp); } nodep->iterateChildren(*this); diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index a49c5d9f8..50bfd6a25 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -109,6 +109,12 @@ private: nodep->iterateChildren(*this); m_ftaskp = NULL; } + virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (nodep->taskp() && nodep->taskp()->dpiContext()) { + nodep->scopeNamep(new AstScopeName(nodep->fileline())); + } + } virtual void visit(AstSenItem* nodep, AstNUser*) { // Remove bit selects, and bark if it's not a simple variable diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index fcad81a91..8eecff05c 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -211,16 +211,14 @@ private: } 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() - string prefix = (string)(".")+m_scopep->prettyName(); + string prefix = (string)("__DOT__")+m_scopep->name(); // TOP and above will be the user's name(). - // Note 'TOP.'is stripped by prettyName, but not 'TOP'. - if (prefix != ".TOP") { - // To keep correct visual order, must add before other Text's - AstNode* afterp = nodep->scopeAttrp(); - if (afterp) afterp->unlinkFrBackWithNext(); - nodep->scopeAttrp(new AstText(nodep->fileline(), prefix)); - if (afterp) nodep->scopeAttrp(afterp); - } + // Note 'TOP.' is stripped by scopePrettyName + // To keep correct visual order, must add before other Text's + AstNode* afterp = nodep->scopeAttrp(); + if (afterp) afterp->unlinkFrBackWithNext(); + nodep->scopeAttrp(new AstText(nodep->fileline(), prefix)); + if (afterp) nodep->scopeAttrp(afterp); nodep->iterateChildren(*this); } virtual void visit(AstScope* nodep, AstNUser*) { diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp index 2b0fa4815..6f59e4695 100644 --- a/src/V3Signed.cpp +++ b/src/V3Signed.cpp @@ -99,8 +99,9 @@ private: virtual void visit(AstLogIf* nodep, AstNUser*) { signed_Ou_Ix(nodep); } virtual void visit(AstLogIff* nodep, AstNUser*) { signed_Ou_Ix(nodep); } // ... These shouldn't matter, just make unsigned - virtual void visit(AstUCFunc* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstScopeName* nodep, AstNUser*) { signed_Ou_Ix(nodep); } virtual void visit(AstText* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstUCFunc* nodep, AstNUser*) { signed_Ou_Ix(nodep); } // ... These comparisons don't care about inbound types // ... (Though they should match. We don't check.) virtual void visit(AstEq* nodep, AstNUser*) { signed_Ou_Ix(nodep); } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 4f1a3c5cc..a3d38350b 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -324,6 +324,16 @@ private: return level; } + AstVarScope* createInputVar(AstCFunc* funcp, const string& name, AstBasicDTypeKwd kwd) { + AstVar* newvarp = new AstVar (funcp->fileline(), AstVarType::BLOCKTEMP, name, + new AstBasicDType(funcp->fileline(), kwd)); + newvarp->funcLocal(true); + newvarp->combineType(AstVarType::INPUT); + funcp->addArgsp(newvarp); + AstVarScope* newvscp = new AstVarScope(funcp->fileline(), m_scopep, newvarp); + m_scopep->addVarp(newvscp); + return newvscp; + } AstVarScope* createVarScope(AstVar* invarp, const string& name) { // We could create under either the ref's scope or the ftask's scope. // It shouldn't matter, as they are only local variables. @@ -491,8 +501,19 @@ private: } } // First argument is symbol table, then output if a function - bool needContext = !refp->taskp()->dpiImport() || refp->taskp()->dpiContext(); - if (needContext) ccallp->argTypes("vlSymsp"); + bool needSyms = !refp->taskp()->dpiImport(); + if (needSyms) ccallp->argTypes("vlSymsp"); + + if (refp->taskp()->dpiContext()) { + // __Vscopep + AstNode* snp = refp->scopeNamep()->unlinkFrBack(); if (!snp) refp->v3fatalSrc("Missing scoping context"); + ccallp->addArgsp(snp); + // __Vfilenamep + ccallp->addArgsp(new AstCMath(refp->fileline(), "\""+refp->fileline()->filename()+"\"", 64, true)); + // __Vlineno + ccallp->addArgsp(new AstConst(refp->fileline(), refp->fileline()->lineno())); + } + if (outvscp) { ccallp->addArgsp(new AstVarRef(refp->fileline(), outvscp, true)); } @@ -536,7 +557,10 @@ private: for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = stmtp->castVar()) { AstVarScope* portvscp = portp->user2p()->castNode()->castVarScope(); // Remembered when we created it earlier - if (portp->isIO() && !portp->isFuncReturn() && portvscp != rtnvscp) { + if (portp->isIO() && !portp->isFuncReturn() && portvscp != rtnvscp + && portp->name() != "__Vscopep" // Passed to dpiContext, not callee + && portp->name() != "__Vfilenamep" + && portp->name() != "__Vlineno") { bool bitvec = (portp->basicp()->isBitLogic() && portp->width() > 32); if (args != "") { args+= ", "; } @@ -576,7 +600,8 @@ private: // Store context, if needed if (nodep->dpiContext()) { - // TBD + string stmt = "Verilated::dpiContext(__Vscopep, __Vfilenamep, __Vlineno);\n"; + cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt)); } {// Call the user function @@ -640,8 +665,11 @@ private: string prefix = ""; if (nodep->dpiImport()) prefix = "__Vdpiimwrap_"; else if (ftaskNoInline) prefix = "__VnoInFunc_"; + // Unless public, v3Descope will not uniquify function names even if duplicate per-scope. + string suffix = ""; // So, make them unique + if (!nodep->taskPublic()) suffix = "_"+m_scopep->nameDotless(); AstCFunc* cfuncp = new AstCFunc(nodep->fileline(), - prefix + nodep->name(), + prefix + nodep->name() + suffix, m_scopep, ((nodep->taskPublic() && rtnvarp)?rtnvarp->cpubArgType(true,true):"")); // It's ok to combine imports because this is just a wrapper; duplicate wrappers can get merged. @@ -652,8 +680,8 @@ private: cfuncp->pure (nodep->pure()); //cfuncp->dpiImport // Not set in the wrapper - the called function has it set - bool needContext = !nodep->dpiImport() || nodep->dpiContext(); - if (needContext) { + bool needSyms = !nodep->dpiImport(); + if (needSyms) { if (nodep->taskPublic()) { // We need to get a pointer to all of our variables (may have eval'ed something else earlier) cfuncp->addInitsp( @@ -664,6 +692,13 @@ private: cfuncp->argTypes(EmitCBaseVisitor::symClassVar()); } } + if (nodep->dpiContext()) { + // First three args go to dpiContext call + createInputVar (cfuncp, "__Vscopep", AstBasicDTypeKwd::SCOPEPTR); + createInputVar (cfuncp, "__Vfilenamep", AstBasicDTypeKwd::CHARPTR); + createInputVar (cfuncp, "__Vlineno", AstBasicDTypeKwd::INT); + } + // Fake output variable if was a function if (rtnvarp) cfuncp->addArgsp(rtnvarp); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index b0ff0ef40..5ee399e13 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -481,7 +481,7 @@ private: nodep->width(selwidth,selwidth); } } - virtual void visit(AstAttrOf* nodep, AstNUser*) { + virtual void visit(AstAttrOf* nodep, AstNUser*) { nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p()); nodep->width(32,1); // Approximation, unsized 32 } @@ -489,7 +489,7 @@ private: // Only used in CStmts which don't care.... } virtual void visit(AstScopeName* nodep, AstNUser* vup) { - // Only used in Displays which don't care.... + nodep->width(64,1); // A pointer, but not that it matters } virtual void visit(AstArrayDType* nodep, AstNUser* vup) { // Lower datatype determines the width diff --git a/test_regress/t/t_dpi_context.pl b/test_regress/t/t_dpi_context.pl new file mode 100755 index 000000000..016a5768a --- /dev/null +++ b/test_regress/t/t_dpi_context.pl @@ -0,0 +1,19 @@ +#!/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 ( + v_flags2 => ["t/t_dpi_context_c.cpp"], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_dpi_context.v b/test_regress/t/t_dpi_context.v new file mode 100644 index 000000000..f2cff60a2 --- /dev/null +++ b/test_regress/t/t_dpi_context.v @@ -0,0 +1,52 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 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. + +module t (); + + sub a (.inst(1)); + sub b (.inst(2)); + + initial begin + a.test1; + b.test1; + a.test2; + b.test2; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule + +module sub (input integer inst); + + import "DPI-C" context function int dpic_line(); + import "DPI-C" context function int dpic_save(int value); + import "DPI-C" context function int dpic_restore(); + + int result; + + task test1; + // Check line numbering +`ifndef verilator // Not all sims support SV2009 `__LINE__, and some that do fail the specific-line test + result = dpic_line(); if (!result) $stop; +`else + result = dpic_line(); if (result !== `__LINE__) $stop; + // + result = dpic_line(); if (result !== `__LINE__) $stop; +`endif + + // Check save-restore + result = dpic_save(23+inst); + if (result==0) $stop; + endtask + + task test2; + if (dpic_restore() != 23+inst) $stop; + endtask + +endmodule diff --git a/test_regress/t/t_dpi_context_c.cpp b/test_regress/t/t_dpi_context_c.cpp new file mode 100644 index 000000000..04c7063e3 --- /dev/null +++ b/test_regress/t/t_dpi_context_c.cpp @@ -0,0 +1,104 @@ +// -*- C++ -*- +//************************************************************************* +// +// Copyright 2009-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. +// +// Verilator is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +//************************************************************************* + +#include +#include + +//====================================================================== + +#if defined(VERILATOR) +# ifdef T_DPI_CONTEXT_NOOPT +# include "Vt_dpi_context_noopt__Dpi.h" +# else +# include "Vt_dpi_context__Dpi.h" +# endif +#elif defined(VCS) +# include "../vc_hdrs.h" +#elif defined(CADENCE) +# define NEED_EXTERNS +#else +# error "Unknown simulator for DPI test" +#endif + +#ifdef NEED_EXTERNS +extern "C" { + + extern int dpic_line(); + extern int dpic_save(int value); + extern int dpic_restore(); +} +#endif + +//====================================================================== + +int dpic_line() { + svScope scope = svGetScope(); + if (!scope) { + printf("%%Warning: svGetScope failed\n"); + return 0; + } + + const char* scopenamep = svGetNameFromScope(scope); + if (!scopenamep) { + printf("%%Warning: svGetNameFromScope failed\n"); + return 0; + } + if (scope != svGetScopeFromName(scopenamep)) { + printf("%%Warning: svGetScopeFromName repeat failed\n"); + return 0; + } + + const char* filenamep = ""; + int lineno = 0; + if (svGetCallerInfo(&filenamep, &lineno)) { + printf("Call from %s:%d:%s\n", filenamep, lineno, scopenamep); + } else { + printf("%%Warning: svGetCallerInfo failed\n"); + return 0; + } + return lineno; +} + +extern int Dpic_Unique; +int Dpic_Unique = 0; // Address used for uniqueness + +int dpic_save(int value) { + svScope scope = svGetScope(); + if (!scope) { + printf("%%Warning: svGetScope failed\n"); + return 0; + } + + if (svPutUserData(scope, &Dpic_Unique, (void*)(value))) { + printf("%%Warning: svPutUserData failed\n"); + return 0; + } + return 1; +} + +int dpic_restore() { + svScope scope = svGetScope(); + if (!scope) { + printf("%%Warning: svGetScope failed\n"); + return 0; + } + + if (void* userp = svGetUserData(scope, &Dpic_Unique)) { + return (int)(long long)(userp); + } else { + printf("%%Warning: svGetUserData failed\n"); + return 0; + } +} diff --git a/test_regress/t/t_dpi_context_noopt.pl b/test_regress/t/t_dpi_context_noopt.pl new file mode 100755 index 000000000..d02a1a2b4 --- /dev/null +++ b/test_regress/t/t_dpi_context_noopt.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +top_filename("t/t_dpi_context.v"); + +compile ( + v_flags2 => ["t/t_dpi_context_c.cpp"], + verilator_flags2 => [$Self->{v3}?"-O0":""], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_dpi_import.v b/test_regress/t/t_dpi_import.v index fe77e8866..0588dd2c3 100644 --- a/test_regress/t/t_dpi_import.v +++ b/test_regress/t/t_dpi_import.v @@ -54,9 +54,6 @@ module t (); import "DPI-C" dpii_fa_bit = function int oth_f_int1(input int i); import "DPI-C" dpii_fa_bit = function int oth_f_int2(input int i); - // Try context - import "DPI-C" context function int dpii_context(); - bit i_b, o_b; bit [7:0] i_b8, o_b8; bit [8:0] i_b9, o_b9; @@ -131,12 +128,6 @@ module t (); if (oth_f_int1(32'd123) !== ~32'd123) $stop; if (oth_f_int2(32'd124) !== ~32'd124) $stop; -`ifndef verilator // Not all sims support SV2009 `__LINE__, and some that do fail the specific-line test - if (!dpii_context()) $stop; -`else - //UNSUP if (dpii_context() !== `__LINE__) $stop; -`endif - $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_dpi_import_c.cpp b/test_regress/t/t_dpi_import_c.cpp index 81830068b..c8d46c9a1 100644 --- a/test_regress/t/t_dpi_import_c.cpp +++ b/test_regress/t/t_dpi_import_c.cpp @@ -58,8 +58,6 @@ extern "C" { extern int dpii_t_int (int i, int *o); extern int dpii_fa_bit(int i); - - extern int dpii_context(); } #endif @@ -109,13 +107,3 @@ int dpii_t_int (int i, int *o) { int dpii_fa_bit (int i) { return ~i; } - -int dpii_context() { - const char* filename = ""; - int lineno = 0; - if (svGetCallerInfo(&filename, &lineno)) { - return lineno; - } else { - return 0; - } -}