Support DPI context imports

This commit is contained in:
Wilson Snyder 2009-12-05 10:38:49 -05:00
parent 2dc7b7ad78
commit 17bf13fcb6
25 changed files with 596 additions and 118 deletions

View File

@ -23,7 +23,8 @@
///
//=========================================================================
#include "verilated.h"
#define _VERILATED_CPP_
#include "verilatedimp.h"
#include <cctype>
#include <vector>
@ -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<string> 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; i<argc; i++) {
VerilatedImp::s_argVec.push_back(argv[i]);
}
// Can't just test later for empty vector, no arguments is ok
VerilatedImp::s_argVecLoaded = true;
VerilatedImp::commandArgs(argc,argv);
}
//===========================================================================
@ -957,4 +926,22 @@ VerilatedModule::~VerilatedModule() {
if (m_namep) free((void*)m_namep); m_namep=NULL;
}
//======================================================================
// VerilatedScope:: Methods
VerilatedScope::~VerilatedScope() {
VerilatedImp::scopeErase(this);
delete [] m_namep; m_namep = NULL;
}
void VerilatedScope::configure(const char* prefixp, const char* suffixp) {
// Slow ok - called once/scope at construction
// We don't want the space and reference-count access overhead of strings.
char* namep = new char[strlen(prefixp)+strlen(suffixp)+2];
strcpy(namep, prefixp);
strcat(namep, suffixp);
m_namep = namep;
VerilatedImp::scopeInsert(this);
}
//===========================================================================

View File

@ -52,6 +52,7 @@ typedef WData* WDataOutP; ///< Array output from a function
class SpTraceVcd;
class SpTraceVcdCFile;
class VerilatedScope;
//=========================================================================
/// Base class for all Verilated module classes
@ -126,16 +127,37 @@ public:
//===========================================================================
/// Verilator global static information class
class VerilatedScope {
const char* m_namep; // Scope name
public:
// Internals - called from VerilatedModule's
VerilatedScope() {}
~VerilatedScope();
void configure(const char* prefixp, const char* suffixp);
// Accessors
const char* name() const { return m_namep; }
};
//===========================================================================
/// Verilator global static information class
struct Verilated {
// Extern Vars
// Below two are used as bool, but having as uint32_t avoids conversion time
// MEMBERS
private:
static int s_randReset; ///< Random reset: 0=all 0s, 1=all 1s, 2=random
static int s_debug; ///< See accessors... only when VL_DEBUG set
static bool s_calcUnusedSigs; ///< Waves file on, need all signals calculated
static bool s_gotFinish; ///< A $finish statement executed
static bool s_assertOn; ///< Assertions are enabled
static VL_THREAD const VerilatedScope* t_dpiScopep; ///< DPI context scope
static VL_THREAD const char* t_dpiFilename; ///< DPI context filename
static VL_THREAD int t_dpiLineno; ///< DPI context line number
public:
// METHODS - User called
/// Select initial value of otherwise uninitialized signals.
////
/// 0 = Set to zeros
@ -151,8 +173,6 @@ public:
#else
static inline int debug() { return 0; } ///< Constant 0 debug, so C++'s optimizer rips up
#endif
/// Internal: Create a new module name by concatenating two strings
static const char* catName(const char* n1, const char* n2); // Returns new'ed data
/// Enable calculation of unused signals
static void calcUnusedSigs(bool flag) { s_calcUnusedSigs=flag; }
static bool calcUnusedSigs() { return s_calcUnusedSigs; } ///< Return calcUnusedSigs value
@ -166,9 +186,22 @@ public:
/// Enable/disable assertions
static void assertOn(bool flag) { s_assertOn=flag; }
static bool assertOn() { return s_assertOn; }
/// Command line arguments
/// Record command line arguments, for retrieval by $test$plusargs/$value$plusargs
static void commandArgs(int argc, const char** argv);
static void commandArgs(int argc, char** argv) { commandArgs(argc,(const char**)argv); }
// METHODS - INTERNAL USE ONLY
// Internal: Create a new module name by concatenating two strings
static const char* catName(const char* n1, const char* n2); // Returns new'ed data
// Internal: Get and set DPI context
static const VerilatedScope* dpiScope() { return t_dpiScopep; }
static void dpiScope(const VerilatedScope* scopep) { scopep=t_dpiScopep; }
static void dpiContext(const VerilatedScope* scopep, const char* filenamep, int lineno) {
t_dpiScopep=scopep; t_dpiFilename=filenamep; t_dpiLineno=lineno; }
static void dpiClearContext() { t_dpiScopep = NULL; }
static bool dpiInContext() { return t_dpiScopep != NULL; }
static const char* dpiFilenamep() { return t_dpiFilename; }
static int dpiLineno() { return t_dpiLineno; }
};
//=========================================================================

View File

@ -23,19 +23,27 @@
///
//=========================================================================
#define _VERILATEDDPI_CPP_
#include "verilatedos.h"
#include "verilated.h"
#include "verilatedimp.h"
#include "svdpi.h"
//======================================================================
// Internal macros
#define _VL_SVDPI_UNIMP()
// Not supported yet
#define _VL_SVDPI_UNIMP() \
vl_fatal(__FILE__,__LINE__,"",Verilated::catName("%%Error: Unsupported DPI function: ",__func__))
// Function requires a "context" in the import declaration
#define _VL_SVDPI_CONTEXT_WARN()
#define _VL_SVDPI_CONTEXT_WARN() \
VL_PRINTF("%%Warning: DPI C Function called by Verilog DPI import with missing 'context' keyword.\n");
//======================================================================
//======================================================================
//======================================================================
// DPI ROUTINES
// Version
const char* svDpiVersion() {
@ -245,36 +253,39 @@ void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int ind
// Functions for working with DPI context
svScope svGetScope() {
_VL_SVDPI_UNIMP(); return NULL;
if (VL_UNLIKELY(!Verilated::dpiInContext())) { _VL_SVDPI_CONTEXT_WARN(); return NULL; }
return (svScope)Verilated::dpiScope();
}
svScope svSetScope(const svScope scope) {
_VL_SVDPI_UNIMP(); return NULL;
const VerilatedScope* vscopep = (const VerilatedScope*)(scope);
Verilated::dpiScope(vscopep);
return (svScope)vscopep;
}
const char* svGetNameFromScope(const svScope) {
_VL_SVDPI_UNIMP(); return "";
const char* svGetNameFromScope(const svScope scope) {
const VerilatedScope* vscopep = (const VerilatedScope*)(scope);
return vscopep->name();
}
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;
}
//======================================================================

143
include/verilatedimp.h Normal file
View File

@ -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 <map>
#include <vector>
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<string> ArgVec;
typedef map<pair<const void*,void*>,void*> UserMap;
typedef map<const char*, const VerilatedScope*, VerilatedCStrCmp> 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 <scope name, scope pointer>
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; i<argc; i++) s_s.m_argVec.push_back(argv[i]);
s_s.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok
}
static string argPlusMatch(const char* prefixp) {
int len = strlen(prefixp);
if (VL_UNLIKELY(!s_s.m_argVecLoaded)) {
s_s.m_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_s.m_argVec.begin(); it!=s_s.m_argVec.end(); ++it) {
if ((*it)[0]=='+') {
if (0==strncmp(prefixp, it->c_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

View File

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

View File

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

View File

@ -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<<" \""<<out<<"\"";
}
void AstCFile::dump(ostream& str) {
this->AstNode::dump(str);
if (source()) str<<" [SRC]";

View File

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

View File

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

View File

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

View File

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

View File

@ -44,10 +44,13 @@ class EmitCSyms : EmitCBaseVisitor {
// AstNodeModule::user1() -> bool. Set true __Vconfigure called
AstUser1InUse m_inuser1;
typedef map<string,AstScopeName*> ScopeNames;
// STATE
AstNodeModule* m_modp; // Current module
typedef pair<AstScope*,AstNodeModule*> ScopeModPair;
vector<ScopeModPair> 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<ScopeModPair>::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");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

19
test_regress/t/t_dpi_context.pl Executable file
View File

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

View File

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

View File

@ -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 <stdio.h>
#include <svdpi.h>
//======================================================================
#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;
}
}

View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
top_filename("t/t_dpi_context.v");
compile (
v_flags2 => ["t/t_dpi_context_c.cpp"],
verilator_flags2 => [$Self->{v3}?"-O0":""],
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

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

View File

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