From 045ff25f808e24732b692d20b50d97e19a0fd308 Mon Sep 17 00:00:00 2001 From: Stefan Wallentowitz Date: Tue, 1 Oct 2019 21:57:45 -0400 Subject: [PATCH] Support vpiModule, bug1469. Add very basic support for vpiModule. Basically it allows to traverse the module tree to find a variable etc. It does not support more than vpi_iterate and vpi_scan for vpiModule along basic operations like vpi_get_str on vpiModule. The support is added non-intrusively to non-VPI verilator runs. It essentially: - Tracks the creation of cell instances and keeps them alive until the emit phase. They are there converted to scopes if modules. - Emits empty (don't add anything during construction) VerilatedScopes for all inlined modules, only for those inlined modules that are on the hierarchical path to public variables. - Adds VerilatedHierarchy as abstraction to structure of the scopes. It is only created for VPI designs. It allows to traverse the hierarchy from the top (NULL). Signed-off-by: Stefan Wallentowitz Signed-off-by: Wilson Snyder --- Changes | 2 + include/verilated.cpp | 10 +- include/verilated.h | 19 +++- include/verilated_imp.h | 18 ++- include/verilated_syms.h | 10 ++ include/verilated_vpi.cpp | 70 +++++++++++- src/V3AstNodes.h | 10 +- src/V3EmitC.cpp | 1 + src/V3EmitCSyms.cpp | 196 +++++++++++++++++++++++++------- src/V3LinkDot.cpp | 2 +- src/V3Scope.cpp | 3 + test_regress/t/t_vpi_module.cpp | 189 ++++++++++++++++++++++++++++++ test_regress/t/t_vpi_module.pl | 30 +++++ test_regress/t/t_vpi_module.v | 124 ++++++++++++++++++++ 14 files changed, 634 insertions(+), 50 deletions(-) create mode 100644 test_regress/t/t_vpi_module.cpp create mode 100755 test_regress/t/t_vpi_module.pl create mode 100644 test_regress/t/t_vpi_module.v diff --git a/Changes b/Changes index abf2b9543..bb21c1f9f 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,8 @@ The contributors that suggested a given feature are shown in []. Thanks! *** Add --public-flat-rw, bug1511. [Stefan Wallentowitz] +*** Support vpiModule, bug1469. [Stefan Wallentowitz] + **** Fix make test with no VERILATOR_ROOT, bug1494. [Ahmed El-Mahmoudy] **** Make Syms file honor --output-split-cfuncs, bug1499. [Todd Strader] diff --git a/include/verilated.cpp b/include/verilated.cpp index c583e5668..fb253c286 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -2101,6 +2101,7 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VerilatedScope::VerilatedScope() { m_callbacksp = NULL; m_namep = NULL; + m_identifierp = NULL; m_funcnumMax = 0; m_symsp = NULL; m_varsp = NULL; @@ -2116,15 +2117,18 @@ VerilatedScope::~VerilatedScope() { } void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, - const char* suffixp) VL_MT_UNSAFE { + const char* suffixp, const char* identifier, + const Type type) VL_MT_UNSAFE { // Slowpath - called once/scope at construction // We don't want the space and reference-count access overhead of strings. m_symsp = symsp; + m_type = type; char* namep = new char[strlen(prefixp)+strlen(suffixp)+2]; strcpy(namep, prefixp); if (*prefixp && *suffixp) strcat(namep, "."); strcat(namep, suffixp); m_namep = namep; + m_identifierp = identifier; VerilatedImp::scopeInsert(this); } @@ -2229,6 +2233,10 @@ void VerilatedScope::scopeDump() const { } } +void VerilatedHierarchy::add(VerilatedScope* fromp, VerilatedScope* top) { + VerilatedImp::hierarchyAdd(fromp, top); +} + //=========================================================================== // VerilatedOneThreaded:: Methods diff --git a/include/verilated.h b/include/verilated.h index 0004e244a..68c09de84 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -203,6 +203,8 @@ public: //========================================================================= /// Base class for all Verilated module classes +class VerilatedScope; + class VerilatedModule { VL_UNCOPYABLE(VerilatedModule); private: @@ -290,6 +292,11 @@ public: // But for internal use only /// This class is initialized by main thread only. Reading post-init is thread safe. class VerilatedScope { +public: + typedef enum { + SCOPE_MODULE, SCOPE_OTHER + } Type; // Type of a scope, currently module is only interesting +private: // Fastpath: VerilatedSyms* m_symsp; ///< Symbol table void** m_callbacksp; ///< Callback table pointer (Fastpath) @@ -297,16 +304,20 @@ class VerilatedScope { // 4 bytes padding (on -m64), for rent. VerilatedVarNameMap* m_varsp; ///< Variable map const char* m_namep; ///< Scope name (Slowpath) + const char* m_identifierp; ///< Identifier of scope (with escapes removed) + Type m_type; ///< Type of the scope public: // But internals only - called from VerilatedModule's VerilatedScope(); ~VerilatedScope(); - void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp) VL_MT_UNSAFE; + void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffix, + const char* identifier, const Type type) VL_MT_UNSAFE; void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE; void varInsert(int finalize, const char* namep, void* datap, VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE; // ACCESSORS const char* name() const { return m_namep; } + const char* identifier() const { return m_identifierp; } inline VerilatedSyms* symsp() const { return m_symsp; } VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT; VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; } @@ -322,6 +333,12 @@ public: // But internals only - called from VerilatedModule's return scopep->exportFindError(funcnum); // LCOV_EXCL_LINE } } + Type type() { return m_type; } +}; + +class VerilatedHierarchy { +public: + void add(VerilatedScope* fromp, VerilatedScope* top); }; //=========================================================================== diff --git a/include/verilated_imp.h b/include/verilated_imp.h index 35eb12d0a..5fa61103e 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -24,7 +24,7 @@ #ifndef _VERILATED_IMP_H_ #define _VERILATED_IMP_H_ 1 ///< Header Guard -#if !defined(_VERILATED_CPP_) && !defined(_VERILATED_DPI_CPP_) +#if !defined(_VERILATED_CPP_) && !defined(_VERILATED_DPI_CPP_) && !defined(_VERILATED_VPI_CPP_) # error "verilated_imp.h only to be included by verilated*.cpp internals" #endif @@ -187,6 +187,10 @@ class VerilatedImp { VerilatedMutex m_nameMutex; ///< Protect m_nameMap VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); ///< Map of + + VerilatedMutex m_hierMapMutex; ///< Protect m_hierMap + VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex); ///< Map the represents scope hierarchy + // Slow - somewhat static: VerilatedMutex m_exportMutex; ///< Protect m_nameMap ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex); ///< Map of @@ -321,6 +325,18 @@ public: // But only for verilated*.cpp return &s_s.m_nameMap; } +public: // But only for verilated*.cpp + // METHODS - hierarchy + static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE { + // Slow ok - called at construction for VPI accessible elements + VerilatedLockGuard lock(s_s.m_hierMapMutex); + s_s.m_hierMap[fromp].push_back(top); + } + static const VerilatedHierarchyMap* hierarchyMap() VL_MT_SAFE_POSTINIT { + // Thread save only assuming this is called only after model construction completed + return &s_s.m_hierMap; + } + public: // But only for verilated*.cpp // METHODS - export names diff --git a/include/verilated_syms.h b/include/verilated_syms.h index 8c7e463f2..dc7398090 100644 --- a/include/verilated_syms.h +++ b/include/verilated_syms.h @@ -36,6 +36,7 @@ #include "verilated_sym_props.h" #include +#include //====================================================================== /// Types @@ -63,4 +64,13 @@ public: ~VerilatedVarNameMap() {} }; +typedef std::vector VerilatedScopeVector; + +class VerilatedHierarchyMap + : public std::map { +public: + VerilatedHierarchyMap() {} + ~VerilatedHierarchyMap() {} +}; + #endif // Guard diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index c35930ac0..e533e3bf1 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -24,17 +24,20 @@ /// Code available from: http://www.veripool.org/verilator /// //========================================================================= - + +#define _VERILATED_VPI_CPP_ #if VM_SC # include "verilated_sc.h" #endif #include "verilated.h" #include "verilated_vpi.h" +#include "verilated_imp.h" #include #include #include +#include //====================================================================== // Internal constants @@ -307,6 +310,45 @@ public: } }; +class VerilatedVpioModule : public VerilatedVpio { + const VerilatedScope* m_modulep; + const char* m_name; + const char* m_fullname; +public: + explicit VerilatedVpioModule(const VerilatedScope* modulep) + : m_modulep(modulep) { + m_fullname = m_modulep->name(); + if (strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4; + m_name = m_modulep->identifier(); + } + static inline VerilatedVpioModule* castp(vpiHandle h) { + return dynamic_cast((VerilatedVpio*)h); } + virtual vluint32_t type() const { return vpiModule; } + const VerilatedScope* modulep() const { return m_modulep; } + virtual const char* name() const { return m_name; } + virtual const char* fullname() const { return m_fullname; } +}; + +class VerilatedVpioModuleIter : public VerilatedVpio { + const std::vector *m_vec; + std::vector::const_iterator m_it; +public: + explicit VerilatedVpioModuleIter(const std::vector& vec) : m_vec(&vec) { + m_it = m_vec->begin(); + } + virtual ~VerilatedVpioModuleIter() {} + static inline VerilatedVpioModuleIter* castp(vpiHandle h) { + return dynamic_cast((VerilatedVpio*) h); } + virtual vluint32_t type() const { return vpiIterator; } + virtual vpiHandle dovpi_scan() { + if (m_it == m_vec->end()) { + return 0; + } + const VerilatedScope* modp = *m_it++; + return (new VerilatedVpioModule(modp))->castVpiHandle(); + } +}; + //====================================================================== struct VerilatedVpiTimedCbsCmp { @@ -999,7 +1041,7 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { if (VL_UNLIKELY(!namep)) return NULL; VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope);); VerilatedVpioScope* voScopep = VerilatedVpioScope::castp(scope); - const VerilatedVar* varp; + const VerilatedVar* varp = NULL; const VerilatedScope* scopep; std::string scopeAndName = namep; if (voScopep) { @@ -1019,9 +1061,19 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { baseNamep = dotp+1; scopename = std::string(namep, dotp-namep); } - scopep = Verilated::scopeFind(scopename.c_str()); - if (!scopep) return NULL; - varp = scopep->varFind(baseNamep); + + if (scopename.find(".") == std::string::npos) { + // This is a toplevel, hence search in our TOP ports first. + scopep = Verilated::scopeFind("TOP"); + if (scopep) { + varp = scopep->varFind(baseNamep); + } + } + if (!varp) { + scopep = Verilated::scopeFind(scopename.c_str()); + if (!scopep) return NULL; + varp = scopep->varFind(baseNamep); + } } if (!varp) return NULL; return (new VerilatedVpioVar(varp, scopep))->castVpiHandle(); @@ -1133,6 +1185,14 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { return ((new VerilatedVpioVarIter(vop->scopep())) ->castVpiHandle()); } + case vpiModule: { + VerilatedVpioModule* vop = VerilatedVpioModule::castp(object); + const VerilatedHierarchyMap* map = VerilatedImp::hierarchyMap(); + const VerilatedScope *mod = vop ? vop->modulep() : NULL; + VerilatedHierarchyMap::const_iterator it = map->find((VerilatedScope*) mod); + if (it == map->end()) return 0; + return ((new VerilatedVpioModuleIter(it->second))->castVpiHandle()); + } default: _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", VL_FUNC, VerilatedVpiError::strFromVpiObjType(type)); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 63d9acba2..744608116 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -1879,21 +1879,27 @@ public: class AstCellInline : public AstNode { // A instantiation cell that was removed by inlining - // For communication between V3Inline and V3LinkDot only + // For communication between V3Inline and V3LinkDot, + // except for VPI runs where it exists until the end. + // It is augmented with the scope in V3Scope for VPI. // Children: When 2 levels inlined, other CellInline under this private: string m_name; // Cell name, possibly {a}__DOT__{b}... string m_origModName; // Original name of the module, ignoring name() changes, for dot lookup + AstScope* m_scopep; // The scope that the cell is inlined into public: AstCellInline(FileLine* fl, const string& name, const string& origModName) : AstNode(fl) - , m_name(name), m_origModName(origModName) {} + , m_name(name), m_origModName(origModName), m_scopep(NULL) {} ASTNODE_NODE_FUNCS(CellInline) virtual void dump(std::ostream& str); + virtual const char* broken() const { BROKEN_RTN(m_scopep && !m_scopep->brokeExists()); return NULL; } // ACCESSORS virtual string name() const { return m_name; } // * = Cell name string origModName() const { return m_origModName; } // * = modp()->origName() before inlining virtual void name(const string& name) { m_name = name; } + void scopep(AstScope* scp) { m_scopep = scp; } + AstScope* scopep() const { return m_scopep; } }; class AstCellRef : public AstNode { diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 453fac0ea..cb30ca236 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -853,6 +853,7 @@ public: virtual void visit(AstTraceDecl*) {} // Handled outside the Visit class virtual void visit(AstTraceInc*) {} // Handled outside the Visit class virtual void visit(AstCFile*) {} // Handled outside the Visit class + virtual void visit(AstCellInline*) {} // Handled outside the Visit class (EmitCSyms) // Default virtual void visit(AstNode* nodep) { puts(string("\n???? // ")+nodep->prettyTypeName()+"\n"); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 1d747220b..cbd0afc33 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -43,9 +43,9 @@ class EmitCSyms : EmitCBaseVisitor { AstUser1InUse m_inuser1; // TYPES - struct ScopeNameData { string m_symName; string m_prettyName; - ScopeNameData(const string& symName, const string& prettyName) - : m_symName(symName), m_prettyName(prettyName) {} + struct ScopeData { string m_symName; string m_prettyName; string m_type; + ScopeData(const string& symName, const string& prettyName, const string& type) + : m_symName(symName), m_prettyName(prettyName), m_type(type) {} }; struct ScopeFuncData { AstScopeName* m_scopep; AstCFunc* m_funcp; AstNodeModule* m_modp; ScopeFuncData(AstScopeName* scopep, AstCFunc* funcp, AstNodeModule* modp) @@ -60,9 +60,11 @@ class EmitCSyms : EmitCBaseVisitor { }; typedef std::map ScopeFuncs; typedef std::map ScopeVars; - typedef std::map ScopeNames; + typedef std::map ScopeNames; typedef std::pair ScopeModPair; typedef std::pair ModVarPair; + typedef std::vector ScopeNameList; + typedef std::map ScopeNameHierarchy; struct CmpName { inline bool operator() (const ScopeModPair& lhsp, const ScopeModPair& rhsp) const { return lhsp.first->name() < rhsp.first->name(); @@ -81,12 +83,14 @@ class EmitCSyms : EmitCBaseVisitor { // STATE AstCFunc* m_funcp; // Current function AstNodeModule* m_modp; // Current module - std::vector m_scopes; // Every scope by module + std::vector m_scopes; // Every scope by module std::vector m_dpis; // DPI functions std::vector m_modVars; // Each public {mod,var} ScopeNames m_scopeNames; // Each unique AstScopeName ScopeFuncs m_scopeFuncs; // Each {scope,dpi-export-func} ScopeVars m_scopeVars; // Each {scope,public-var} + ScopeNames m_vpiScopeCandidates; // All scopes for VPI + ScopeNameHierarchy m_vpiScopeHierarchy; // The actual hierarchy of scopes V3LanguageWords m_words; // Reserved word detector int m_coverBins; // Coverage bin number int m_labelNum; // Next label number @@ -119,6 +123,57 @@ class EmitCSyms : EmitCBaseVisitor { } } + string scopeSymString(const string& scpname) { + string out = scpname; + string::size_type pos; + while ((pos = out.find("__PVT__")) != string::npos) { + out.replace(pos, 7, ""); + } + if (out.substr(0, 10) == "TOP__DOT__") out.replace(0, 10, ""); + if (out.substr(0, 4) == "TOP.") out.replace(0, 4, ""); + while ((pos = out.find('.')) != string::npos) { + out.replace(pos, 1, "__"); + } + while ((pos = out.find("__DOT__")) != string::npos) { + out.replace(pos, 7, "__"); + } + return out; + } + + string scopeDecodeIdentifier(const string& scpname) { + string out = scpname; + // Remove hierarchy + string::size_type pos = out.rfind("."); + if (pos != std::string::npos) out.erase(0, pos + 1); + // Decode all escaped characters + while ((pos = out.find("__0")) != string::npos) { + unsigned int x; + std::stringstream ss; + ss << std::hex << out.substr(pos+3, 2); + ss >> x; + out.replace(pos, 5, 1, (char) x); + } + return out; + } + + void varHierarchyScopes(string scp) { + while (!scp.empty()) { + ScopeNames::const_iterator scpit = m_vpiScopeCandidates.find(scp); + if ((scpit != m_vpiScopeCandidates.end()) + && (m_scopeNames.find(scp) == m_scopeNames.end())) { + m_scopeNames.insert(make_pair(scpit->second.m_symName, scpit->second)); + } + string::size_type pos = scp.rfind("__DOT__"); + if (pos == string::npos) { + pos = scp.rfind("."); + if (pos == string::npos) { + break; + } + } + scp.resize(pos); + } + } + void varsExpand() { // We didn't have all m_scopes loaded when we encountered variables, so expand them now // It would be less code if each module inserted its own variables. @@ -149,26 +204,14 @@ class EmitCSyms : EmitCBaseVisitor { //UINFO(9,"For "<name()<<" - "<name()<<" Scp "<name(), @@ -178,12 +221,39 @@ class EmitCSyms : EmitCBaseVisitor { } } + void buildVpiHierarchy() { + for (ScopeNames::const_iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { + if (it->second.m_type != "SCOPE_MODULE") continue; + + string name = it->second.m_prettyName; + if (name.substr(0, 4) == "TOP.") name.replace(0, 4, ""); + + string above = name; + while (!above.empty()) { + string::size_type pos = above.rfind("."); + if (pos == string::npos) { + break; + } + above.resize(pos); + if (m_vpiScopeHierarchy.find(above) != m_vpiScopeHierarchy.end()) { + m_vpiScopeHierarchy[above].push_back(name); + break; + } + } + m_vpiScopeHierarchy[name] = std::vector(); + } + } + // VISITORS virtual void visit(AstNetlist* nodep) { // Collect list of scopes iterateChildren(nodep); varsExpand(); + if (v3Global.opt.vpi()) { + buildVpiHierarchy(); + } + // Sort by names, so line/process order matters less stable_sort(m_scopes.begin(), m_scopes.end(), CmpName()); stable_sort(m_dpis.begin(), m_dpis.end(), CmpDpi()); @@ -206,15 +276,33 @@ class EmitCSyms : EmitCBaseVisitor { iterateChildren(nodep); m_modp = NULL; } + virtual void visit(AstCellInline* nodep) { + if (v3Global.opt.vpi()) { + string type = (nodep->origModName() == "__BEGIN__") ? "SCOPE_OTHER" + : "SCOPE_MODULE"; + string name = nodep->scopep()->name() + "__DOT__" + nodep->name(); + string name_dedot = AstNode::dedotName(name); + m_vpiScopeCandidates.insert(make_pair(name, ScopeData(scopeSymString(name), + name_dedot, type))); + } + } virtual void visit(AstScope* nodep) { nameCheck(nodep); + m_scopes.push_back(make_pair(nodep, m_modp)); + + if (v3Global.opt.vpi() && !nodep->isTop()) { + m_vpiScopeCandidates.insert(make_pair(nodep->name(), + ScopeData(scopeSymString(nodep->name()), + nodep->name(), "SCOPE_MODULE"))); + } } virtual void visit(AstScopeName* nodep) { string name = nodep->scopeSymName(); //UINFO(9,"scnameins sp "<name()<<" sp "<scopePrettySymName()<<" ss "<scopePrettySymName()))); + m_scopeNames.insert(make_pair(name, ScopeData(name, nodep->scopePrettySymName(), + "SCOPE_OTHER"))); } if (nodep->dpiExport()) { UASSERT_OBJ(m_funcp, nodep, "ScopeName not under DPI function"); @@ -223,8 +311,9 @@ class EmitCSyms : EmitCBaseVisitor { } else { if (m_scopeNames.find(nodep->scopeDpiName()) == m_scopeNames.end()) { m_scopeNames.insert(make_pair(nodep->scopeDpiName(), - ScopeNameData(nodep->scopeDpiName(), - nodep->scopePrettyDpiName()))); + ScopeData(nodep->scopeDpiName(), + nodep->scopePrettyDpiName(), + "SCOPE_OTHER"))); } } } @@ -353,17 +442,18 @@ void EmitCSyms::emitSymHdr() { puts("uint32_t __Vcoverage["); puts(cvtToStr(m_coverBins)); puts("];\n"); } - { // Scope names - bool did = false; + if (!m_scopeNames.empty()) { // Scope names + puts("\n// SCOPE NAMES\n"); for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { - if (!did) { - did = true; - puts("\n// SCOPE NAMES\n"); - } puts("VerilatedScope __Vscope_"+it->second.m_symName+";\n"); } } + if (v3Global.opt.vpi()) { + puts("\n// SCOPE HIERARCHY\n"); + puts("VerilatedHierarchy __Vhier;\n"); + } + puts("\n// CREATORS\n"); puts(symClassName()+"("+topClassName()+"* topp, const char* namep);\n"); puts(string("~")+symClassName()+"() {}\n"); @@ -550,21 +640,49 @@ void EmitCSyms::emitSymImp() { } } - { // Setup scope names - bool did = false; + if (!m_scopeNames.empty()) { // Setup scope names + puts("// Setup scopes\n"); for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { - if (!did) { - did = true; - puts("// Setup scope names\n"); - } checkSplit(false); puts("__Vscope_"+it->second.m_symName+".configure(this,name(),"); putsQuoted(it->second.m_prettyName); - puts(");\n"); + puts(", "); + putsQuoted(scopeDecodeIdentifier(it->second.m_prettyName)); + puts(", VerilatedScope::"+it->second.m_type+");\n"); ++m_numStmts; } } + if (v3Global.opt.vpi()) { + puts("\n// Setup scope hierarchy\n"); + for (ScopeNames::const_iterator it = m_scopeNames.begin(); + it != m_scopeNames.end(); ++it) { + string name = it->second.m_prettyName; + if (it->first == "TOP") continue; + name = name.replace(0, 4, ""); // Remove the "TOP." + if ((name.find(".") == string::npos) && (it->second.m_type == "SCOPE_MODULE")) { + puts("__Vhier.add(0, &__Vscope_" + it->second.m_symName + ");\n"); + } + } + + for (ScopeNameHierarchy::const_iterator it = m_vpiScopeHierarchy.begin(); + it != m_vpiScopeHierarchy.end(); ++it) { + for (ScopeNameList::const_iterator lit = it->second.begin(); + lit != it->second.end(); ++lit) { + string fromname = scopeSymString(it->first); + string toname = scopeSymString(*lit); + ScopeNames::const_iterator from = m_scopeNames.find(fromname); + ScopeNames::const_iterator to = m_scopeNames.find(toname); + UASSERT(from != m_scopeNames.end(), fromname+" not in m_scopeNames"); + UASSERT(to != m_scopeNames.end(), toname+" not in m_scopeNames"); + puts("__Vhier.add("); + puts("&__Vscope_"+from->second.m_symName+", "); + puts("&__Vscope_"+to->second.m_symName+");\n"); + } + } + puts("\n"); + } + // Everything past here is in the __Vfinal loop, so start a new split file if needed closeSplit(); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 05ea72d1e..b2a5e8350 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1793,7 +1793,7 @@ private: } virtual void visit(AstCellInline* nodep) { checkNoDot(nodep); - if (m_statep->forScopeCreation()) { + if (m_statep->forScopeCreation() && !v3Global.opt.vpi()) { nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); } } diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index 88caf2ac1..473c70a8e 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -151,6 +151,9 @@ private: // ***Note m_scopep is passed back to the caller of the routine (above) } + virtual void visit(AstCellInline* nodep) { + nodep->scopep(m_scopep); + } virtual void visit(AstActive* nodep) { nodep->v3fatalSrc("Actives now made after scoping"); } diff --git a/test_regress/t/t_vpi_module.cpp b/test_regress/t/t_vpi_module.cpp new file mode 100644 index 000000000..50fe633b9 --- /dev/null +++ b/test_regress/t/t_vpi_module.cpp @@ -0,0 +1,189 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2010-2011 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. +// +//************************************************************************* + +#ifdef IS_VPI + +#include "vpi_user.h" +#include + +#else + +#include "Vt_vpi_module.h" +#include "verilated.h" +#include "svdpi.h" + +#include "Vt_vpi_module__Dpi.h" + +#include "verilated_vpi.h" +#include "verilated_vcd_c.h" + +#endif + +#include +#include +#include +using namespace std; + +#include "TestSimulator.h" +#include "TestVpi.h" + +// __FILE__ is too long +#define FILENM "t_vpi_module.cpp" + +#define DEBUG if (0) printf + +unsigned int main_time = false; + +#define CHECK_RESULT_NZ(got) \ + if (!(got)) { \ + printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", FILENM,__LINE__); \ + return __LINE__; \ + } + +#define CHECK_RESULT_CSTR(got, exp) \ + if (strcmp((got), (exp))) { \ + printf("%%Error: %s:%d: GOT = '%s' EXP = '%s'\n", \ + FILENM, __LINE__, (got)?(got):"", (exp)?(exp):""); \ + return __LINE__; \ + } + +extern "C" { + int mon_check() { + vpiHandle it = vpi_iterate(vpiModule, NULL); + CHECK_RESULT_NZ(it); + + vpiHandle topmod = vpi_scan(it); + CHECK_RESULT_NZ(topmod); + + char* name = vpi_get_str(vpiName, topmod); + CHECK_RESULT_NZ(name); + CHECK_RESULT_CSTR(name, "t"); + + it = vpi_iterate(vpiModule, topmod); + CHECK_RESULT_NZ(it); + + vpiHandle mod = vpi_scan(it); + CHECK_RESULT_NZ(mod); + + name = vpi_get_str(vpiName, mod); + CHECK_RESULT_CSTR(name, "mod_a"); + + it = vpi_iterate(vpiModule, mod); + CHECK_RESULT_NZ(it); + + mod = vpi_scan(it); + CHECK_RESULT_NZ(mod); + + name = vpi_get_str(vpiName, mod); + if (strcmp(name, "mod_b") == 0) { + // Full visibility in other simulators, skip mod_b + mod = vpi_scan(it); + CHECK_RESULT_NZ(mod); + name = vpi_get_str(vpiName, mod); + } + CHECK_RESULT_CSTR(name, "mod_c."); + + return 0; // Ok + } +} +//====================================================================== + +#ifdef IS_VPI + +static int mon_check_vpi() { + vpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = { + {vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0 +}; + +// cver entry +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++); +} + +// icarus entry +void (*vlog_startup_routines[])() = { + vpi_compat_bootstrap, + 0 +}; + +#else + +double sc_time_stamp() { + return main_time; +} +int main(int argc, char **argv, char **env) { + double sim_time = 1100; + Verilated::commandArgs(argc, argv); + Verilated::debug(0); + // we're going to be checking for these errors do don't crash out + Verilated::fatalOnVpiError(0); + + VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out + +#ifdef VERILATOR +# ifdef TEST_VERBOSE + Verilated::scopesDump(); +# endif +#endif + +#if VM_TRACE + Verilated::traceEverOn(true); + VL_PRINTF("Enabling waves...\n"); + VerilatedVcdC* tfp = new VerilatedVcdC; + topp->trace(tfp, 99); + tfp->open(STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); +#endif + + topp->eval(); + topp->clk = 0; + main_time += 10; + + while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + main_time += 1; + topp->eval(); + VerilatedVpi::callValueCbs(); + topp->clk = !topp->clk; + //mon_do(); +#if VM_TRACE + if (tfp) tfp->dump (main_time); +#endif + } + if (!Verilated::gotFinish()) { + vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish"); + } + topp->final(); + +#if VM_TRACE + if (tfp) tfp->close(); +#endif + + delete topp; topp=NULL; + exit(0L); +} + +#endif diff --git a/test_regress/t/t_vpi_module.pl b/test_regress/t/t_vpi_module.pl new file mode 100755 index 000000000..9318d6cec --- /dev/null +++ b/test_regress/t/t_vpi_module.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010 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. + +scenarios(simulator => 1); + +skip("Known compiler limitation") + if $Self->cxx_version =~ /\(GCC\) 4.4/; + +compile( + make_top_shell => 0, + make_main => 0, + make_pli => 1, + iv_flags2 => ["-g2005-sv -D USE_VPI_NOT_DPI"], + v_flags2 => ["+define+USE_VPI_NOT_DPI"], + verilator_flags2 => ["-CFLAGS '-DVL_DEBUG -ggdb' --exe --vpi --no-l2name $Self->{t_dir}/t_vpi_module.cpp"], + ); + +execute( + iv_pli => 1, + check_finished => 1 + ); + +ok(1); +1; diff --git a/test_regress/t/t_vpi_module.v b/test_regress/t/t_vpi_module.v new file mode 100644 index 000000000..35be2a9a7 --- /dev/null +++ b/test_regress/t/t_vpi_module.v @@ -0,0 +1,124 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2010 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. + +`ifdef USE_VPI_NOT_DPI +//We call it via $c so we can verify DPI isn't required - see bug572 +`else +import "DPI-C" context function integer mon_check(); +`endif + +module t (/*AUTOARG*/ + // Inputs + clk + ); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + input clk; + + integer status; + + wire a, b, x; + + A mod_a(/*AUTOINST*/ + // Outputs + .x (x), + // Inputs + .clk (clk), + .a (a), + .b (b)); + + // Test loop + initial begin +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif +`ifdef iverilog + status = $mon_check(); +`endif +`ifndef USE_VPI_NOT_DPI + status = mon_check(); +`endif + if (status!=0) begin + $write("%%Error: t_vpi_module.cpp:%0d: C Test failed\n", status); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule : t + +module A(/*AUTOARG*/ + // Outputs + x, + // Inputs + clk, a, b + ); + + input clk; + + input a, b; + output x; + + wire y, c; + + B mod_b(/*AUTOINST*/ + // Outputs + .y (y), + // Inputs + .b (b), + .c (c)); + + C \mod_c. (/*AUTOINST*/ + // Outputs + .x (x), + // Inputs + .clk (clk), + .a (a), + .y (y)); + +endmodule : A + +module B(/*AUTOARG*/ + // Outputs + y, + // Inputs + b, c + ); /*verilator public_module*/ + input b, c; + + output reg y; + + always @(*) begin : myproc + y = b ^ c; + end + +endmodule + +module C(/*AUTOARG*/ + // Outputs + x, + // Inputs + clk, a, y + ); + + input clk; + + input a, y; + + output reg x /* verilator public_flat_rw */; + + always @(posedge clk) begin + x <= a & y; + end + +endmodule