diff --git a/Changes b/Changes index be004c66e..53a91ce4b 100644 --- a/Changes +++ b/Changes @@ -3,6 +3,10 @@ Revision history for Verilator The contributors that suggested a given feature are shown in []. [by ...] indicates the contributor was also the author of the fix; Thanks! +* Verilator 3.8*** + +*** Support runtime access to public signal names. + * Verilator 3.801 2010/03/17 *** Support "break", "continue", "return". diff --git a/include/verilated.cpp b/include/verilated.cpp index 3506e93a6..3f11d9731 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1017,12 +1017,14 @@ VerilatedScope::VerilatedScope() { m_callbacksp = NULL; m_namep = NULL; m_funcnumMax = 0; + m_varsp = NULL; } VerilatedScope::~VerilatedScope() { VerilatedImp::scopeErase(this); if (m_namep) { delete [] m_namep; m_namep = NULL; } if (m_callbacksp) { delete [] m_callbacksp; m_callbacksp = NULL; } + if (m_varsp) { delete m_varsp; m_varsp = NULL; } m_funcnumMax = 0; // Force callback table to empty } @@ -1058,6 +1060,46 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) { } } +void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, VerilatedVarType vltype, int dims, ...) { + // Grab dimensions + // In the future we may just create a large table at emit time and statically construct from that. + if (!finalize) return; + + if (!m_varsp) m_varsp = new VerilatedVarNameMap(); + VerilatedVar var (namep, datap, vltype); + + va_list ap; + va_start(ap,dims); + for (int i=0; iinsert(make_pair(namep,var)); +} + +VerilatedVar* VerilatedScope::varFind(const char* namep) const { + if (VL_LIKELY(m_varsp)) { + VerilatedVarNameMap::iterator it = m_varsp->find(namep); + if (VL_LIKELY(it != m_varsp->end())) { + return &(it->second); + } + } + return NULL; +} + void* VerilatedScope::exportFindNullError(int funcnum) const { // Slowpath - Called only when find has failed string msg = (string("Testbench C called '") @@ -1085,6 +1127,12 @@ void VerilatedScope::scopeDump() const { m_callbacksp[i], VerilatedImp::exportName(i)); } } + if (varsp()) { + for (VerilatedVarNameMap::const_iterator it = varsp()->begin(); + it != varsp()->end(); ++it) { + VL_PRINTF(" VAR %p: %s\n", &(it->second), it->first); + } + } } //=========================================================================== diff --git a/include/verilated.h b/include/verilated.h index e78318483..522ce699a 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -66,9 +66,22 @@ typedef void (*VerilatedVoidCb)(void); class SpTraceVcd; class SpTraceVcdCFile; +class VerilatedVar; +class VerilatedVarNameMap; class VerilatedVcd; class VerilatedVcdC; +enum VerilatedVarType { + VLVT_UNKNOWN=0, + VLVT_PTR, // Pointer to something + VLVT_UINT8, // AKA CData + VLVT_UINT16, // AKA SData + VLVT_UINT32, // AKA IData + VLVT_UINT64, // AKA QData + VLVT_WDATA, // AKA WData + VLVT_STRING // C++ string +}; + //========================================================================= /// Base class for all Verilated module classes @@ -155,6 +168,7 @@ class VerilatedScope { void** m_callbacksp; ///< Callback table pointer (Fastpath) int m_funcnumMax; ///< Maxium function number stored (Fastpath) // 4 bytes padding (on -m64), for rent. + VerilatedVarNameMap* m_varsp; ///< Variable map const char* m_namep; ///< Scope name (Slowpath) public: // But internals only - called from VerilatedModule's @@ -162,9 +176,12 @@ public: // But internals only - called from VerilatedModule's ~VerilatedScope(); void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp); void exportInsert(int finalize, const char* namep, void* cb); + void varInsert(int finalize, const char* namep, void* datap, VerilatedVarType vltype, int dims, ...); // ACCESSORS const char* name() const { return m_namep; } inline VerilatedSyms* symsp() const { return m_symsp; } + VerilatedVar* varFind(const char* namep) const; + VerilatedVarNameMap* varsp() const { return m_varsp; } void* exportFindError(int funcnum) const; void* exportFindNullError(int funcnum) const; void scopeDump() const; diff --git a/include/verilated_imp.h b/include/verilated_imp.h index 71b03f222..5dd2c6c8f 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -31,6 +31,7 @@ #include "verilatedos.h" #include "verilated.h" #include "verilated_heavy.h" +#include "verilated_syms.h" #include #include @@ -41,13 +42,6 @@ class VerilatedScope; //====================================================================== // Types -struct VerilatedCStrCmp { - // For ordering maps keyed by const char*'s - bool operator() (const char *a, const char *b) const { - return std::strcmp(a, b) < 0; - } -}; - class VerilatedImp { // Whole class is internal use only - Global information shared between verilated*.cpp files. diff --git a/include/verilated_syms.h b/include/verilated_syms.h new file mode 100644 index 000000000..ec11504cb --- /dev/null +++ b/include/verilated_syms.h @@ -0,0 +1,92 @@ +// -*- C++ -*- +//************************************************************************* +// +// Copyright 2003-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. +// +// 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: Include to allow symbol inspection +/// +/// This file is for inclusion by files that need to inspect +/// the symbol table. It is not included in verilated.h +/// as it requires some heavyweight C++ classes. +/// +/// Code available from: http://www.veripool.org/verilator +/// +//************************************************************************* + + +#ifndef _VERILATED_SYMS_H_ +#define _VERILATED_SYMS_H_ 1 ///< Header Guard + +#include "verilated_heavy.h" + +#include + +//====================================================================== +// Types + +struct VerilatedCStrCmp { + /// Ordering maps keyed by const char*'s + bool operator() (const char *a, const char *b) const { + return std::strcmp(a, b) < 0; + } +}; + +//=========================================================================== +/// Verilator range + +class VerilatedRange { + int m_lhs; + int m_rhs; +protected: + friend class VerilatedVar; + friend class VerilatedScope; + VerilatedRange() : m_lhs(0), m_rhs(0) {} + void sets(int lhs, int rhs) { m_lhs=lhs; m_rhs=rhs; } +public: + ~VerilatedRange() {} + int lhs() const { return m_lhs; } + int rhs() const { return m_rhs; } +}; + +//=========================================================================== +/// Verilator variable + +class VerilatedVar { + void* m_datap; // Location of data + VerilatedVarType m_vltype; // Data type + VerilatedRange m_range; // First range + VerilatedRange m_array; // Array + const char* m_namep; // Name - slowpath +protected: + friend class VerilatedScope; + VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype) + : m_datap(datap), m_vltype(vltype), m_namep(namep) {} +public: + ~VerilatedVar() {} + void* datap() const { return m_datap; } + VerilatedVarType vltype() const { return m_vltype; } + const VerilatedRange& range() const { return m_range; } + const VerilatedRange& array() const { return m_array; } + const char* namep() const { return m_namep; } +}; + +//====================================================================== +/// Types + +struct VerilatedVarNameMap : public map { + VerilatedVarNameMap() {} + ~VerilatedVarNameMap() {} +}; + +#endif // Guard diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 39e868aef..0ba219aae 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -137,6 +137,31 @@ string AstVar::vlArgType(bool named, bool forReturn) const { return arg; } +string AstVar::vlEnumType() const { + string arg; + AstBasicDType* bdtypep = basicp(); + bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING; + if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) { + return "VLVT_PTR"; + } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) { + return "VLVT_PTR"; + } else if (strtype) { + arg += "VLVT_STRING"; + } else if (widthMin() <= 8) { + arg += "VLVT_UINT8"; + } else if (widthMin() <= 16) { + arg += "VLVT_UINT16"; + } else if (widthMin() <= VL_WORDSIZE) { + arg += "VLVT_UINT32"; + } else if (isQuad()) { + arg += "VLVT_UINT64"; + } else if (isWide()) { + arg += "VLVT_WDATA"; + } + // else return "VLVT_UNKNOWN" + return arg; +} + string AstVar::cPubArgType(bool named, bool forReturn) const { if (forReturn) named=false; string arg; @@ -348,6 +373,8 @@ string AstScopeName::scopeSymName() const { out += textp->text(); } if (out.substr(0,10) == "__DOT__TOP") out.replace(0,10,""); + if (out.substr(0,7) == "__DOT__") out.replace(0,7,""); + if (out.substr(0,1) == ".") out.replace(0,1,""); string::size_type pos; while ((pos=out.find(".")) != string::npos) { out.replace(pos, 1, "__"); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 0ee8d4512..5568ebfeb 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -542,8 +542,9 @@ private: bool m_sc:1; // SystemC variable bool m_scClocked:1; // SystemC sc_clk<> needed bool m_scSensitive:1;// SystemC sensitive() needed - bool m_sigPublic:1; // User C code accesses this signal + bool m_sigPublic:1; // User C code accesses this signal or is top signal bool m_sigModPublic:1;// User C code accesses this signal and module + bool m_sigUserPublic:1; // User C code accesses this signal bool m_usedClock:1; // Signal used as a clock bool m_usedParam:1; // Parameter is referenced (on link; later signals not setup) bool m_funcLocal:1; // Local variable for a function @@ -561,7 +562,7 @@ private: m_primaryIO=false; m_sc=false; m_scClocked=false; m_scSensitive=false; m_usedClock=false; m_usedParam=false; - m_sigPublic=false; m_sigModPublic=false; + m_sigPublic=false; m_sigModPublic=false; m_sigUserPublic=false; m_funcLocal=false; m_funcReturn=false; m_attrClockEn=false; m_attrIsolateAssign=false; m_attrSFormat=false; m_fileDescr=false; m_isConst=false; m_isStatic=false; @@ -607,6 +608,7 @@ public: string cPubArgType(bool named, bool forReturn) const; // Return C /*public*/ type for argument: bool, uint32_t, uint64_t, etc. string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument string vlArgType(bool named, bool forReturn) const; // Return Verilator internal type for argument: CData, SData, IData, WData + string vlEnumType() const; // Return VerilatorImp enum name for argument: VLVT_UINT32, etc void combineType(AstVarType type); AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType) @@ -628,6 +630,7 @@ public: void usedParam(bool flag) { m_usedParam = flag; } void sigPublic(bool flag) { m_sigPublic = flag; } void sigModPublic(bool flag) { m_sigModPublic = flag; } + void sigUserPublic(bool flag) { m_sigUserPublic = flag; if (flag) m_sigPublic = flag; } void sc(bool flag) { m_sc = flag; } void scSensitive(bool flag) { m_scSensitive = flag; } void primaryIO(bool flag) { m_primaryIO = flag; } @@ -670,6 +673,7 @@ public: bool isScSensitive() const { return m_scSensitive; } bool isSigPublic() const; bool isSigModPublic() const { return m_sigModPublic; } + bool isSigUserPublic() const { return m_sigUserPublic; } bool isTrace() const { return m_trace; } bool isConst() const { return m_isConst; } bool isStatic() const { return m_isStatic; } @@ -699,6 +703,7 @@ public: combineType(typevarp->varType()); if (typevarp->isSigPublic()) sigPublic(true); if (typevarp->isSigModPublic()) sigModPublic(true); + if (typevarp->isSigUserPublic()) sigUserPublic(true); if (typevarp->attrScClocked()) attrScClocked(true); } void inlineAttrReset(const string& name) { diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 5f1626ff0..2c0504c62 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -45,13 +45,24 @@ 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 ScopeFuncData { AstScopeName* m_scopep; AstCFunc* m_funcp; AstNodeModule* m_modp; ScopeFuncData(AstScopeName* scopep, AstCFunc* funcp, AstNodeModule* modp) : m_scopep(scopep), m_funcp(funcp), m_modp(modp) {} }; + struct ScopeVarData { string m_scopeName; string m_varBasePretty; AstVar* m_varp; + AstNodeModule* m_modp; AstScope* m_scopep; + ScopeVarData(const string& scopeName, const string& varBasePretty, AstVar* varp, AstNodeModule* modp, AstScope* scopep) + : m_scopeName(scopeName), m_varBasePretty(varBasePretty), m_varp(varp), m_modp(modp), m_scopep(scopep) {} + }; typedef map ScopeFuncs; - typedef map ScopeNames; + typedef map ScopeVars; + typedef map ScopeNames; typedef pair ScopeModPair; + typedef pair ModVarPair; struct CmpName { inline bool operator () (const ScopeModPair& lhsp, const ScopeModPair& rhsp) const { return lhsp.first->name() < rhsp.first->name(); @@ -71,8 +82,10 @@ class EmitCSyms : EmitCBaseVisitor { AstNodeModule* m_modp; // Current module vector m_scopes; // Every scope by module vector m_dpis; // DPI functions + vector m_modVars; // Each public {mod,var} ScopeNames m_scopeNames; // Each unique AstScopeName - ScopeFuncs m_scopeFuncs; // Each {scope,dpiexportfunc} + ScopeFuncs m_scopeFuncs; // Each {scope,dpi-export-func} + ScopeVars m_scopeVars; // Each {scope,public-var} V3LanguageWords m_words; // Reserved word detector int m_coverBins; // Coverage bin number int m_labelNum; // Next label number @@ -95,10 +108,65 @@ class EmitCSyms : EmitCBaseVisitor { } } + void varsExpand() { + // We didn'e 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. + // Someday. For now public isn't common. + for (vector::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { + AstScope* scopep = it->first; AstNodeModule* smodp = it->second; + for (vector::iterator it = m_modVars.begin(); it != m_modVars.end(); ++it) { + AstNodeModule* modp = it->first; + if (modp == smodp) { + AstVar* varp = it->second; + // Need to split the module + var name into the original-ish full scope and variable name under that scope. + // The module instance name is included later, when we know the scopes this module is under + string whole = scopep->name()+"__DOT__"+varp->name(); + string scpName; + string varBase; + if (whole.substr(0,10) == "__DOT__TOP") whole.replace(0,10,""); + string::size_type pos = whole.rfind("__DOT__"); + if (pos != string::npos) { + scpName = whole.substr(0,pos); + varBase = whole.substr(pos+strlen("__DOT__")); + } else { + varBase = whole; + } + //UINFO(9,"For "<name()<<" - "<name()<<" Scp "<name(), + ScopeVarData(scpSym, varBasePretty, varp, modp, scopep))); + } + } + } + } + // VISITORS virtual void visit(AstNetlist* nodep, AstNUser*) { // Collect list of scopes nodep->iterateChildren(*this); + varsExpand(); // Sort by names, so line/process order matters less sort(m_scopes.begin(), m_scopes.end(), CmpName()); @@ -125,8 +193,9 @@ class EmitCSyms : EmitCBaseVisitor { } virtual void visit(AstScopeName* nodep, AstNUser*) { string name = nodep->scopeSymName(); + //UINFO(9,"scnameins sp "<name()<<" sp "<scopePrettyName()<<" ss "<scopePrettyName()))); } if (nodep->dpiExport()) { if (!m_funcp) nodep->v3fatalSrc("ScopeName not under DPI function"); @@ -134,6 +203,12 @@ class EmitCSyms : EmitCBaseVisitor { ScopeFuncData(nodep, m_funcp, m_modp))); } } + virtual void visit(AstVar* nodep, AstNUser*) { + nodep->iterateChildren(*this); + if (nodep->isSigUserPublic()) { + m_modVars.push_back(make_pair(m_modp, 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 @@ -255,7 +330,7 @@ void EmitCSyms::emitSymHdr() { 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("VerilatedScope __Vscope_"+it->second.m_symName+";\n"); } puts("\n// CREATORS\n"); @@ -345,8 +420,8 @@ void EmitCSyms::emitSymImp() { puts("// Setup scope names\n"); for (ScopeNames::iterator it = m_scopeNames.begin(); it != m_scopeNames.end(); ++it) { - puts("__Vscope_"+it->second->scopeSymName()+".configure(this,name(),"); - putsQuoted(it->second->scopePrettyName()); + puts("__Vscope_"+it->second.m_symName+".configure(this,name(),"); + putsQuoted(it->second.m_prettyName); puts(");\n"); } @@ -367,6 +442,55 @@ void EmitCSyms::emitSymImp() { puts("));\n"); } } + // It would be less code if each module inserted its own variables. + // Someday. For now public isn't common. + for (ScopeVars::iterator it = m_scopeVars.begin(); it != m_scopeVars.end(); ++it) { + AstNodeModule* modp = it->second.m_modp; + AstScope* scopep = it->second.m_scopep; + AstVar* varp = it->second.m_varp; + // + int dim=0; + string bounds; + if (AstBasicDType* basicp = varp->basicp()) { + // Range is always first, it's not in "C" order + if (basicp->rangep()) { + bounds += " ,"; bounds += cvtToStr(basicp->rangep()->msbConst()); + bounds += ","; bounds += cvtToStr(basicp->rangep()->lsbConst()); + dim++; + } + for (AstNodeDType* dtypep=varp->dtypep(); dtypep; ) { + dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node + if (AstArrayDType* adtypep = dtypep->castArrayDType()) { + bounds += " ,"; bounds += cvtToStr(adtypep->arrayp()->msbConst()); + bounds += ","; bounds += cvtToStr(adtypep->arrayp()->lsbConst()); + dim++; + dtypep = adtypep->dtypep(); + } + else break; // AstBasicDType - nothing below, 1 + } + } + // + if (dim>2) { + puts("//UNSUP "); // VerilatedImp can't deal with >2d arrays + } + puts("__Vscope_"+it->second.m_scopeName+".varInsert(__Vfinal,"); + putsQuoted(it->second.m_varBasePretty); + puts(", &("); + if (modp->isTop()) { + puts(scopep->nameDotless()); + puts("p->"); + } else { + puts(scopep->nameDotless()); + puts("."); + } + puts(varp->name()); + puts("), "); + puts(varp->vlEnumType()); // VLVT_UINT32 etc + puts(","); + puts(cvtToStr(dim)); + puts(bounds); + puts(");\n"); + } puts("}\n"); } diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index c665629dd..aa9c8c6ab 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -57,6 +57,7 @@ private: // AstNodeModule::user1p() // bool. True to inline this module (from InlineMarkVisitor) // Cleared each cell // AstVar::user2p() // AstVarRef*/AstConst* Points to signal this is a direct connect to + // AstVar::user3() // bool Don't alias the user4, keep it as signal // STATE AstNodeModule* m_modp; // Current module @@ -151,6 +152,10 @@ private: UINFO(6,"One-to-one "<user2p(connectRefp); + // Public output inside the cell must go via an assign rather than alias + // Else the public logic will set the alias, loosing the value to be propagated up + // (InOnly isn't a problem as the AssignAlias will create the assignment for us) + pinNewVarp->user3(pinNewVarp->isSigUserPublic() && pinNewVarp->isOutOnly()); } // Cleanup var names, etc, to not conflict m_cellp = nodep; @@ -175,6 +180,7 @@ private: // user2p is either a const or a var. AstConst* exprconstp = nodep->user2p()->castNode()->castConst(); AstVarRef* exprvarrefp = nodep->user2p()->castNode()->castVarRef(); + UINFO(1,"connectto: "<user2p()->castNode()<v3fatalSrc("Unknown interconnect type; pinReconnectSimple should have cleared up\n"); } @@ -182,6 +188,15 @@ private: m_modp->addStmtp(new AstAssignW(nodep->fileline(), new AstVarRef(nodep->fileline(), nodep, true), exprconstp->cloneTree(true))); + } else if (nodep->user3()) { + // Public variable at the lower module end - we need to make sure we propagate + // the logic changes up and down; if we aliased, we might remove the change detection + // on the output variable. + UINFO(9,"public pin assign: "<isInput()) nodep->v3fatalSrc("Outputs only - inputs use AssignAlias"); + m_modp->addStmtp(new AstAssignW(nodep->fileline(), + new AstVarRef(nodep->fileline(), exprvarrefp->varp(), true), + new AstVarRef(nodep->fileline(), nodep, false))); } else { m_modp->addStmtp(new AstAssignAlias(nodep->fileline(), new AstVarRef(nodep->fileline(), nodep, true), @@ -216,8 +231,9 @@ private: } virtual void visit(AstVarRef* nodep, AstNUser*) { if (m_cellp) { - if (nodep->varp()->user2p() // It's being converted to a alias. - && !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases + if (nodep->varp()->user2p() // It's being converted to an alias. + && !nodep->varp()->user3() + && !nodep->backp()->castAssignAlias()) { // Don't constant propagate aliases (we just made) AstConst* exprconstp = nodep->varp()->user2p()->castNode()->castConst(); AstVarRef* exprvarrefp = nodep->varp()->user2p()->castNode()->castVarRef(); if (exprconstp) { diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 517265d12..3a8251ac9 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -274,12 +274,12 @@ private: } else if (nodep->attrType() == AstAttrType::VAR_PUBLIC) { if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->sigPublic(true); m_varp->sigModPublic(true); + m_varp->sigUserPublic(true); m_varp->sigModPublic(true); nodep->unlinkFrBack()->deleteTree(); nodep=NULL; } else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT) { if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->sigPublic(true); + m_varp->sigUserPublic(true); nodep->unlinkFrBack()->deleteTree(); nodep=NULL; } else if (nodep->attrType() == AstAttrType::VAR_ISOLATE_ASSIGNMENTS) { diff --git a/test_regress/t/t_dpi_var.cpp b/test_regress/t/t_dpi_var.cpp new file mode 100644 index 000000000..4a608dc88 --- /dev/null +++ b/test_regress/t/t_dpi_var.cpp @@ -0,0 +1,147 @@ +// -*- C++ -*- +//************************************************************************* +// +// Copyright 2010-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. +// +// 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 "Vt_dpi_var.h" +#include "verilated.h" +#include "svdpi.h" + +#include "verilated_syms.h" + +//====================================================================== + +struct MyMon { + vluint32_t* sigsp[2]; + MyMon() { sigsp[0]=NULL; sigsp[1]=NULL; } +}; +MyMon mons[2]; + +void mon_register_a(const char* namep, void* sigp, bool isOut) { + // Callback from initial block in monitor +#ifdef TEST_VERBOSE + VL_PRINTF("- mon_register_a(\"%s\", %p, %d);\n", namep, sigp, isOut); +#endif + mons[0].sigsp[isOut] = (vluint32_t*)sigp; +} + +void mon_do(MyMon* monp) { + if (!monp->sigsp[0]) vl_fatal(__FILE__,__LINE__,"","never registered"); + if (!monp->sigsp[1]) vl_fatal(__FILE__,__LINE__,"","never registered"); + *monp->sigsp[1] = (*(monp->sigsp[0]))+1; + +#ifdef TEST_VERBOSE + VL_PRINTF("- mon_do(%08x(&%p) -> %08x(&%p));\n", + *(monp->sigsp[0]), monp->sigsp[0], *(monp->sigsp[1]), monp->sigsp[1]); +#endif +} + +void mon_class_name(const char* namep) { +#ifdef TEST_VERBOSE + VL_PRINTF("- mon_class_name(\"%s\");\n", namep); +#endif + // Check the C's calling name of "" doesn't lead to extra dots in the name() + if (namep && namep[0]=='.') vl_fatal(__FILE__,__LINE__,"", (string("Unexp class name ")+namep).c_str()); +} + +extern "C" void mon_scope_name(const char* namep); +void mon_scope_name(const char* namep) { + const char* modp = svGetNameFromScope(svGetScope()); +#ifdef TEST_VERBOSE + VL_PRINTF("- mon_scope_name('%s', \"%s\");\n", modp, namep); +#endif + if (strcmp(namep,"t.sub")) vl_fatal(__FILE__,__LINE__,"", (string("Unexp scope name ")+namep).c_str()); + if (strcmp(modp,"t.sub")) vl_fatal(__FILE__,__LINE__,"", (string("Unexp dpiscope name ")+modp).c_str()); +} + +extern "C" void mon_register_b(const char* namep, int isOut); +void mon_register_b(const char* namep, int isOut) { + const char* modp = svGetNameFromScope(svGetScope()); +#ifdef TEST_VERBOSE + VL_PRINTF("- mon_register_b('%s', \"%s\", %d);\n", modp, namep, isOut); +#endif + // Use scope to get pointer and size of signal + const VerilatedScope* scopep = Verilated::dpiScope(); + const VerilatedVar* varp = scopep->varFind(namep); + if (!varp) { + VL_PRINTF("%%Warning: mon_register_b signal not found: \"%s\"\n", namep); + } else if (varp->vltype() != VLVT_UINT32) { + VL_PRINTF("%%Warning: wrong type for signal: \"%s\"\n", namep); + } else { + vluint32_t* datap = (vluint32_t*)(varp->datap()); + VL_PRINTF("- mon_register_b('%s', \"%s\", %p, %d);\n", modp, namep, datap, isOut); + mons[1].sigsp[isOut] = (vluint32_t*)(varp->datap()); + } +} + +extern "C" void mon_register_done(); +void mon_register_done() { + const char* modp = svGetNameFromScope(svGetScope()); +#ifdef TEST_VERBOSE + VL_PRINTF("- mon_register_done('%s');\n", modp); +#endif + // Print list of all signals - if we didn't register2 anything we'd pick them off here + const VerilatedScope* scopep = Verilated::dpiScope(); + if (VerilatedVarNameMap* varsp = scopep->varsp()) { + for (VerilatedVarNameMap::const_iterator it = varsp->begin(); + it != varsp->end(); ++it) { + VL_PRINTF("- mon2: %s\n", it->first); + } + } +} + +bool mon_eval() { + // Callback from always@ negedge + mon_do(&mons[0]); + mon_do(&mons[1]); + return false; +} + +//====================================================================== + +unsigned int main_time = false; + +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); + + VM_PREFIX* topp = new VM_PREFIX (""); // Note null name - we're flattening it out + +#ifdef VERILATOR +# ifdef TEST_VERBOSE + Verilated::scopesDump(); +# endif +#endif + + topp->eval(); + topp->clk = 0; + main_time += 10; + + while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + main_time += 1; + topp->eval(); + topp->clk = !topp->clk; + //mon_do(); + } + if (!Verilated::gotFinish()) { + vl_fatal(__FILE__,__LINE__,"main", "%Error: Timeout; never got a $finish"); + } + topp->final(); + + delete topp; topp=NULL; + exit(0L); +} diff --git a/test_regress/t/t_dpi_var.pl b/test_regress/t/t_dpi_var.pl new file mode 100755 index 000000000..e674bcf17 --- /dev/null +++ b/test_regress/t/t_dpi_var.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 ( + make_top_shell => 0, + make_main => 0, + verilator_flags2 => ["--exe --no-l2name $Self->{t_dir}/t_dpi_var.cpp"], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_dpi_var.v b/test_regress/t/t_dpi_var.v new file mode 100644 index 000000000..c83765273 --- /dev/null +++ b/test_regress/t/t_dpi_var.v @@ -0,0 +1,100 @@ +// 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. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + integer cyc=0; + + wire monclk = ~clk; + + int in; + int fr_a; + int fr_b; + int fr_chk; + sub sub (.*); + + // Test loop + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d in=%x fr_a=%x b=%x fr_chk=%x\n",$time, cyc, in, fr_a, fr_b, fr_chk); +`endif + cyc <= cyc + 1; + in <= {in[30:0], in[31]^in[2]^in[0]}; + if (cyc==0) begin + // Setup + in <= 32'hd70a4497; + end + else if (cyc<3) begin + end + else if (cyc<10) begin + if (fr_chk != fr_a) $stop; + if (fr_chk != fr_b) $stop; + end + else if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +import "DPI-C" context function void mon_scope_name (input string formatted /*verilator sformat*/ ); +import "DPI-C" context function void mon_register_b(string name, int isOut); +import "DPI-C" context function void mon_register_done(); + +module sub (/*AUTOARG*/ + // Outputs + fr_a, fr_b, fr_chk, + // Inputs + in + ); + +`systemc_imp_header + void mon_class_name(const char* namep); + void mon_register_a(const char* namep, void* sigp, bool isOut); + bool mon_eval(); +`verilog + + input int in /*verilator public_flat*/; + output int fr_a /*verilator public_flat*/; + output int fr_b /*verilator public_flat*/; + output int fr_chk; + + reg [3:0] onearray [1:2] /*verilator public_flat*/; + reg [3:0] twoarray [1:2][3:4] /*verilator public_flat*/; + + always @* fr_chk = in + 1; + + initial begin + // Test the naming + $c("mon_class_name(name());"); + mon_scope_name("%m"); + // Scheme A - pass pointer directly + $c("mon_register_a(\"in\",&",in,",false);"); + $c("mon_register_a(\"fr_a\",&",fr_a,",true);"); + // Scheme B - use VPIish callbacks to see what signals exist + mon_register_b("in", 0); + mon_register_b("fr_b", 1); + mon_register_done(); + end + + always @(posedge t.monclk) begin + // Hack - Write all outputs, so the data will get scheduled to propagate out of this module + // FUTURE Long term, we should detect a public signal has been written + // with a new value, and create an event that flips monclk automatically + if ($c1("mon_eval()")) begin + // This code doesn't execute, just is here so verilator schedules the code. + fr_a = 0; + fr_b = 0; + end + end + +endmodule