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 <stefan@wallentowitz.de>
Signed-off-by: Wilson Snyder <wsnyder@wsnyder.org>
This commit is contained in:
Stefan Wallentowitz 2019-10-01 21:57:45 -04:00 committed by Wilson Snyder
parent 66209d1114
commit 045ff25f80
14 changed files with 634 additions and 50 deletions

View File

@ -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]

View File

@ -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

View File

@ -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);
};
//===========================================================================

View File

@ -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 <scope_name, scope pointer>
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 <export_func_proto, func number>
@ -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

View File

@ -36,6 +36,7 @@
#include "verilated_sym_props.h"
#include <map>
#include <vector>
//======================================================================
/// Types
@ -63,4 +64,13 @@ public:
~VerilatedVarNameMap() {}
};
typedef std::vector<const VerilatedScope*> VerilatedScopeVector;
class VerilatedHierarchyMap
: public std::map<const VerilatedScope*, VerilatedScopeVector> {
public:
VerilatedHierarchyMap() {}
~VerilatedHierarchyMap() {}
};
#endif // Guard

View File

@ -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 <list>
#include <map>
#include <set>
#include <sstream>
//======================================================================
// 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<VerilatedVpioModule*>((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<const VerilatedScope*> *m_vec;
std::vector<const VerilatedScope*>::const_iterator m_it;
public:
explicit VerilatedVpioModuleIter(const std::vector<const VerilatedScope*>& vec) : m_vec(&vec) {
m_it = m_vec->begin();
}
virtual ~VerilatedVpioModuleIter() {}
static inline VerilatedVpioModuleIter* castp(vpiHandle h) {
return dynamic_cast<VerilatedVpioModuleIter*>((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));

View File

@ -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 {

View File

@ -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");

View File

@ -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<string,ScopeFuncData> ScopeFuncs;
typedef std::map<string,ScopeVarData> ScopeVars;
typedef std::map<string,ScopeNameData> ScopeNames;
typedef std::map<string,ScopeData> ScopeNames;
typedef std::pair<AstScope*,AstNodeModule*> ScopeModPair;
typedef std::pair<AstNodeModule*,AstVar*> ModVarPair;
typedef std::vector<string> ScopeNameList;
typedef std::map<string, ScopeNameList> 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<ScopeModPair> m_scopes; // Every scope by module
std::vector<ScopeModPair> m_scopes; // Every scope by module
std::vector<AstCFunc*> m_dpis; // DPI functions
std::vector<ModVarPair> 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 "<<scopep->name()<<" - "<<varp->name()<<" Scp "<<scpName<<" Var "<<varBase<<endl);
string varBasePretty = AstNode::prettyName(varBase);
string scpPretty = AstNode::prettyName(scpName);
string scpSym;
{
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, "__");
}
scpSym = out;
}
string scpSym = scopeSymString(scpName);
//UINFO(9," scnameins sp "<<scpName<<" sp "<<scpPretty<<" ss "<<scpSym<<endl);
if (v3Global.opt.vpi()) {
varHierarchyScopes(scpName);
}
if (m_scopeNames.find(scpSym) == m_scopeNames.end()) {
m_scopeNames.insert(make_pair(scpSym, ScopeNameData(scpSym, scpPretty)));
m_scopeNames.insert(make_pair(scpSym, ScopeData(scpSym, scpPretty,
"SCOPE_OTHER")));
}
m_scopeVars.insert(
make_pair(scpSym + " " + varp->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<string>();
}
}
// 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 "<<nodep->name()<<" sp "<<nodep->scopePrettySymName()<<" ss "<<name<<endl);
if (m_scopeNames.find(name) == m_scopeNames.end()) {
m_scopeNames.insert(make_pair(name, ScopeNameData(name, nodep->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();

View File

@ -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);
}
}

View File

@ -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");
}

View File

@ -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 <cstdlib>
#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 <cstdio>
#include <cstring>
#include <iostream>
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):"<null>", (exp)?(exp):"<null>"); \
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

30
test_regress/t/t_vpi_module.pl Executable file
View File

@ -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;

View File

@ -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