DPI: Support strings in DPI Imports

This commit is contained in:
Wilson Snyder 2010-01-17 15:10:37 -05:00
parent 08b63b4f01
commit 788f69a8c9
19 changed files with 192 additions and 28 deletions

View File

@ -1843,6 +1843,11 @@ full nor unique.
All specify blocks and timing checks are ignored.
=item string
String is supported only to the point that they can be passed to DPI
imports.
=item timeunit, timeprecision
All timing control statements are ignored.

View File

@ -616,6 +616,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
// File I/O
void _VL_VINT_TO_STRING(int obits, char* destoutp, WDataInP sourcep) {
// See also VL_DATA_TO_STRING_NW
int lsb=obits-1;
bool start=true;
char* destp = destoutp;
@ -901,6 +902,29 @@ IData VL_VALUEPLUSARGS_IW(int rbits, const char* prefixp, char fmt, WDataOutP rw
return 1;
}
//===========================================================================
// Heavy functions
string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) {
// See also _VL_VINT_TO_STRING
char destout[VL_TO_STRING_MAX_WORDS*VL_WORDSIZE+1];
int obits = lwords * VL_WORDSIZE;
int lsb=obits-1;
bool start=true;
char* destp = destout;
int len = 0;
for (; lsb>=0; lsb--) {
lsb = (lsb / 8) * 8; // Next digit
IData charval = (lwp[VL_BITWORD_I(lsb)]>>VL_BITBIT_I(lsb)) & 0xff;
if (!start || charval) {
*destp++ = (charval==0)?' ':charval;
len++;
start = false; // Drop leading 0s
}
}
return string(destout, len);
}
//===========================================================================
// Verilated:: Methods

View File

@ -36,7 +36,7 @@
#include <cstdlib>
#include <cstring>
// <iostream> avoided to reduce compile time
// <string> avoided to reduce compile time
// <string> avoided and instead in verilatedheavy.h to reduce compile time
using namespace std;
//=========================================================================

48
include/verilatedheavy.h Normal file
View File

@ -0,0 +1,48 @@
// -*- 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.
//
//*************************************************************************
///
/// \file
/// \brief Verilator: String include for all Verilated C files
///
/// This file is included automatically by Verilator at the top of
/// all C++ files it generates. It is used when strings or other
/// heavyweight types are required; these contents are not part of
/// verilated.h to save compile time when such types aren't used.
///
/// Code available from: http://www.veripool.org/verilator
///
//*************************************************************************
#ifndef _VERILATEDHEAVY_H_
#define _VERILATEDHEAVY_H_ 1 ///< Header Guard
#include "verilated.h"
#include <string>
//======================================================================
extern string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp);
inline string VL_CVT_PACK_STR_NQ(QData lhs) {
IData lw[2]; VL_SET_WQ(lw, lhs);
return VL_CVT_PACK_STR_NW(2, lw);
}
inline string VL_CVT_PACK_STR_NI(IData lhs) {
IData lw[1]; lw[0] = lhs;
return VL_CVT_PACK_STR_NW(1, lw);
}
#endif // Guard

View File

@ -30,6 +30,7 @@
#include "verilatedos.h"
#include "verilated.h"
#include "verilatedheavy.h"
#include <map>
#include <vector>

View File

@ -272,6 +272,9 @@ public:
bool isDpiUnsupported() const {
return (m_e==LOGIC || m_e==TIME || m_e==REALTIME);
}
bool isOpaque() const { // IE not a simple number we can bit optimize
return (m_e==STRING || m_e==SCOPEPTR || m_e==CHARPTR);
}
};
inline bool operator== (AstBasicDTypeKwd lhs, AstBasicDTypeKwd rhs) { return (lhs.m_e == rhs.m_e); }
inline bool operator== (AstBasicDTypeKwd lhs, AstBasicDTypeKwd::en rhs) { return (lhs.m_e == rhs); }
@ -918,6 +921,9 @@ struct AstNodeMath : public AstNode {
virtual string emitC() = 0;
virtual string emitSimpleOperator() { return ""; }
virtual bool cleanOut() = 0; // True if output has extra upper bits zero
// Someday we will generically support data types on every math node
// Until then isOpaque indicates we shouldn't constant optimize this node type
bool isOpaque() { return castCvtPackString(); }
};
struct AstNodeTermop : public AstNodeMath {

View File

@ -108,10 +108,14 @@ string AstVar::vlArgType(bool named, bool forReturn) const {
string arg;
if (isWide() && isInOnly()) arg += "const ";
AstBasicDType* bdtypep = basicp();
bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) {
arg += "const char*";
} else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) {
arg += "const VerilatedScope*";
} else if (strtype) {
if (isInOnly()) arg += "const ";
arg += "string";
} else if (widthMin() <= 8) {
arg += "CData";
} else if (widthMin() <= 16) {
@ -123,11 +127,11 @@ string AstVar::vlArgType(bool named, bool forReturn) const {
} else if (isWide()) {
arg += "WData"; // []'s added later
}
if (isWide()) {
if (isWide() && !strtype) {
arg += " (& "+name();
arg += ")["+cvtToStr(widthWords())+"]";
} else {
if (isOutput()) arg += "&";
if (isOutput() || (strtype && isInput())) arg += "&";
if (named) arg += " "+name();
}
return arg;

View File

@ -228,7 +228,7 @@ private:
setSignedState(signst);
if (!rangep) { // Set based on keyword properties
// V3Width will pull from this width
if (keyword().width() > 1) rangep = new AstRange(fileline(), keyword().width()-1, 0);
if (keyword().width() > 1 && !isOpaque()) rangep = new AstRange(fileline(), keyword().width()-1, 0);
width(keyword().width(), keyword().width());
} else {
widthFrom(rangep); // Maybe unknown if parameters underneath it
@ -254,6 +254,7 @@ public:
virtual int widthTotalBytes() const; // (Slow) recurses - Width in bytes rounding up 1,2,4,8,12,...
AstBasicDTypeKwd keyword() const { return m_keyword; } // Avoid using - use isSomething accessors instead
bool isBitLogic() const { return keyword().isBitLogic(); }
bool isOpaque() const { return keyword().isOpaque(); }
bool isSloppy() const { return keyword().isSloppy(); }
bool isZeroInit() const { return keyword().isZeroInit(); }
int msb() const { if (!rangep()) return 0; return rangep()->msbConst(); }
@ -2420,6 +2421,20 @@ public:
int size() const { return m_size; }
};
struct AstCvtPackString : public AstNodeUniop {
// Convert to Verilator Packed Pack (aka Pack)
AstCvtPackString(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {
width(64,64); } // Really, width should be dtypep -> STRING
ASTNODE_NODE_FUNCS(CvtPackString, CVTPACKSTRING)
virtual void numberOperate(V3Number& out, const V3Number& lhs) { V3ERROR_NA; }
virtual string emitVerilog() { return "%f$_CAST(%l)"; }
virtual string emitC() { return "VL_CVT_PACK_STR_N%lq(%lW, %li)"; }
virtual bool cleanOut() {return true;} virtual bool cleanLhs() {return true;}
virtual bool sizeMattersLhs() {return false;}
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(AstNode* samep) const { return true; }
};
struct AstFEof : public AstNodeUniop {
AstFEof(FileLine* fl, AstNode* lhsp) : AstNodeUniop(fl, lhsp) {}
ASTNODE_NODE_FUNCS(FEof, FEOF)

View File

@ -1090,6 +1090,11 @@ private:
nodep->v3error("Expecting expression to be constant, but variable isn't const: "<<nodep->itemp()->prettyName());
}
}
// virtual void visit(AstCvtPackString* nodep, AstNUser*) {
// Not constant propagated (for today) because AstMath::isOpaque is set
// Someday if lower is constant, convert to quoted "string".
virtual void visit(AstAttrOf* nodep, AstNUser*) {
// Don't iterate children, don't want to loose VarRef.
if (nodep->attrType()==AstAttrType::BITS) {
@ -1504,7 +1509,7 @@ private:
TREEOP1("AstSel{warnSelect(nodep)}", "NEVER");
// Generic constants on both side. Do this first to avoid other replacements
TREEOP("AstNodeBiop {$lhsp.castConst, $rhsp.castConst}", "replaceConst(nodep)");
TREEOP("AstNodeUniop{$lhsp.castConst}", "replaceConst(nodep)");
TREEOP("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque()}", "replaceConst(nodep)");
// Zero on one side or the other
TREEOP("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
TREEOP("AstAnd {$lhsp.isZero, $rhsp}", "replaceZero(nodep)");

View File

@ -861,6 +861,14 @@ void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
+","+cvtToStr(basicp->widthWords()));
puts(");\n");
}
} else if (basicp && basicp->isOpaque()) {
// strings and other fundamental c types
puts(nodep->vlArgType(true,false));
// This isn't very robust and may need cleanup for other data types
for (AstArrayDType* arrayp=nodep->dtypeSkipRefp()->castArrayDType(); arrayp; arrayp = arrayp->dtypeSkipRefp()->castArrayDType()) {
puts("["+cvtToStr(arrayp->elementsConst())+"]");
}
puts(";\n");
} else {
// Arrays need a small alignment, but may need different padding after.
// For example three VL_SIG8's needs alignment 1 but size 3.
@ -1226,6 +1234,9 @@ void EmitCImp::emitVarResets(AstNodeModule* modp) {
if (varp->isIO() && modp->isTop() && optSystemC()) {
// System C top I/O doesn't need loading, as the lower level subinst code does it.
}
else if (varp->basicp() && varp->basicp()->keyword() == AstBasicDTypeKwd::STRING) {
// Constructor deals with it
}
else if (varp->isParam()) {
if (!varp->hasSimpleInit()) nodep->v3fatalSrc("No init for a param?");
//puts("// parameter "+varp->name()+" = "+varp->initp()->name()+"\n");
@ -1483,8 +1494,9 @@ void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& pref
// But for now, Smallest->largest makes it more likely a small offset will allow access to the signal.
for (int isstatic=1; isstatic>=0; isstatic--) {
if (prefixIfImp!="" && !isstatic) continue;
for (int size=0; size<8; size++) {
if (size==3) continue;
const int sortmax = 9;
for (int sort=0; sort<sortmax; sort++) {
if (sort==3) continue;
for (AstNode* nodep=firstp; nodep; nodep = nodep->nextp()) {
if (AstVar* varp = nodep->castVar()) {
bool doit = true;
@ -1498,15 +1510,16 @@ void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& pref
if (varp->isStatic() ? !isstatic : isstatic) doit=false;
if (doit) {
int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes();
int sortbytes = 7;
int sortbytes = sortmax-1;
if (varp->isUsedClock() && varp->widthMin()==1) sortbytes = 0;
else if (varp->dtypeSkipRefp()->castArrayDType()) sortbytes=7;
else if (varp->dtypeSkipRefp()->castArrayDType()) sortbytes=8;
else if (varp->basicp() && varp->basicp()->isOpaque()) sortbytes=7;
else if (varp->isScBv()) sortbytes=6;
else if (sigbytes==8) sortbytes=5;
else if (sigbytes==4) sortbytes=4;
else if (sigbytes==2) sortbytes=2;
else if (sigbytes==1) sortbytes=1;
if (size==sortbytes) {
if (sort==sortbytes) {
emitVarDecl(varp, prefixIfImp);
}
}
@ -1556,7 +1569,11 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
}
ofp()->putsIntTopInclude();
puts("#include \"verilated.h\"\n");
if (v3Global.needHeavy()) {
puts("#include \"verilatedheavy.h\"\n");
} else {
puts("#include \"verilated.h\"\n");
}
if (v3Global.opt.coverage()) {
puts("#include \"SpCoverage.h\"\n");
}

View File

@ -47,6 +47,7 @@ class EmitCInlines : EmitCBaseVisitor {
// VISITORS
virtual void visit(AstVar* nodep, AstNUser*) {
// All wide constants load into variables, so we can just hunt for them
nodep->iterateChildren(*this);
if (nodep->widthWords() >= EMITCINLINES_NUM_CONSTW ) {
if (int(m_wordWidths.size()) <= nodep->widthWords()) {
m_wordWidths.resize(nodep->widthWords()+5);
@ -55,6 +56,11 @@ class EmitCInlines : EmitCBaseVisitor {
v3Global.needHInlines(true);
}
}
virtual void visit(AstBasicDType* nodep, AstNUser*) {
if (nodep->keyword() == AstBasicDTypeKwd::STRING) {
v3Global.needHeavy(true); // #include <string> via verilatedheavy.h when we create symbol file
}
}
// NOPs
virtual void visit(AstNodeStmt*, AstNUser*) {}

View File

@ -181,6 +181,12 @@ void EmitCSyms::emitSymHdr() {
puts("#define _"+symClassName()+"_H_\n");
puts("\n");
if (v3Global.needHeavy()) {
puts("#include \"verilatedheavy.h\"\n");
} else {
puts("#include \"verilated.h\"\n");
}
// for
puts("\n// INCLUDE MODULE CLASSES\n");
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {

View File

@ -44,6 +44,7 @@ class V3Global {
int m_debugFileNumber; // Number to append to debug files created
bool m_assertWidthsSame; // Tree should have width()==widthMin()
bool m_needHInlines; // Need __Inlines file
bool m_needHeavy; // Need verilatedheavy.h include
bool m_dpi; // Need __Dpi include files
public:
@ -57,6 +58,7 @@ public:
m_debugFileNumber = 0;
m_assertWidthsSame = false;
m_needHInlines = false;
m_needHeavy = false;
m_dpi = false;
}
void clear() {
@ -78,6 +80,8 @@ public:
}
bool needHInlines() const { return m_needHInlines; }
void needHInlines(bool flag) { m_needHInlines=flag; }
bool needHeavy() const { return m_needHeavy; }
void needHeavy(bool flag) { m_needHeavy=flag; }
bool dpi() const { return m_dpi; }
void dpi(bool flag) { m_dpi = flag; }
};

View File

@ -503,15 +503,6 @@ private:
}
nodep->iterateChildren(*this);
}
virtual void visit(AstBasicDType* nodep, AstNUser*) {
if (m_idState==ID_RESOLVE && nodep->keyword()==AstBasicDTypeKwd::STRING) {
if (!(m_ftaskp && m_ftaskp->dpiImport() && 0/*UNIMP*/)) {
nodep->v3error("Unsupported: SystemVerilog 'string' anywhere but DPI import __format");
}
}
nodep->iterateChildren(*this);
}
virtual void visit(AstCell* nodep, AstNUser*) {
// Cell: Resolve its filename. If necessary, parse it.
m_cellp = nodep;

View File

@ -110,6 +110,8 @@ private:
virtual void visit(AstNeq* nodep, AstNUser*) { signed_Ou_Ix(nodep); }
virtual void visit(AstNeqCase* nodep, AstNUser*){ signed_Ou_Ix(nodep); }
virtual void visit(AstNeqWild* nodep, AstNUser*){ signed_Ou_Ix(nodep); }
// ... Opaque returns, so arbitrary
virtual void visit(AstCvtPackString* nodep, AstNUser*){ signed_Ou_Ix(nodep); }
//========
// Signed: Output signed

View File

@ -592,6 +592,9 @@ private:
stmt += "(void*)";
}
stmt += portp->name()+frSuffix;
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::STRING) {
stmt += ".c_str()";
}
}
stmt += ";\n";
return new AstCStmt(portp->fileline(), stmt);

View File

@ -481,6 +481,9 @@ private:
nodep->width(selwidth,selwidth);
}
}
virtual void visit(AstCvtPackString* nodep, AstNUser* vup) {
nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
}
virtual void visit(AstAttrOf* nodep, AstNUser*) {
nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
nodep->width(32,1); // Approximation, unsized 32
@ -947,11 +950,22 @@ private:
if (accept_mode) {
// Prelim may cause the node to get replaced; we've lost our
// pointer, so need to iterate separately later
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::STRING
&& !pinp->castCvtPackString()
&& !(pinp->castVarRef() && pinp->castVarRef()->varp()->basicp()->keyword()==AstBasicDTypeKwd::STRING)) {
AstNRelinker handle;
pinp->unlinkFrBack(&handle); // No next, that's the next pin
AstNode* newp = new AstCvtPackString(pinp->fileline(), pinp);
handle.relink(newp);
pinp = newp;
}
pinp->accept(*this,WidthVP(portp->width(),portp->widthMin(),PRELIM).p()); pinp=NULL;
} else {
// Do PRELIM again, because above accept may have exited early due to node replacement
pinp->accept(*this,WidthVP(portp->width(),portp->widthMin(),BOTH).p());
widthCheck(nodep,"Function Argument",pinp,portp->width(),portp->widthMin());
if (portp->basicp() && !portp->basicp()->isOpaque()) {
widthCheck(nodep,"Function Argument",pinp,portp->width(),portp->widthMin());
}
}
}
}

View File

@ -38,8 +38,8 @@ module t ();
import "DPI-C" pure function shortint dpii_f_shortint (input shortint i);
import "DPI-C" pure function longint dpii_f_longint (input longint i);
import "DPI-C" pure function chandle dpii_f_chandle (input chandle i);
`ifndef VERILATOR
import "DPI-C" pure function string dpii_f_string (input string i);
`ifndef VERILATOR
import "DPI-C" pure function real dpii_f_real (input real i);
`endif
`ifndef NO_SHORTREAL
@ -52,14 +52,16 @@ module t ();
import "DPI-C" pure function void dpii_v_shortint (input shortint i, output shortint o);
import "DPI-C" pure function void dpii_v_longint (input longint i, output longint o);
import "DPI-C" pure function void dpii_v_chandle (input chandle i, output chandle o);
`ifndef VERILATOR
import "DPI-C" pure function void dpii_v_string (input string i, output string o);
`ifndef VERILATOR
import "DPI-C" pure function void dpii_v_real (input real i, output real o);
`endif
`ifndef NO_SHORTREAL
import "DPI-C" pure function void dpii_v_shortreal(input shortreal i, output shortreal o);
`endif
import "DPI-C" pure function int dpii_f_strlen (input string i);
import "DPI-C" function void dpii_f_void ();
// Try a task
@ -86,8 +88,8 @@ module t ();
shortint i_s, o_s;
longint i_l, o_l;
chandle i_c, o_c;
`ifndef VERILATOR
string i_n, o_n;
`ifndef VERILATOR
real i_d, o_d;
`endif
`ifndef NO_SHORTREAL
@ -134,8 +136,8 @@ module t ();
if (dpii_f_shortint (i_s) !== ~i_s) $stop;
if (dpii_f_longint (i_l) !== ~i_l) $stop;
if (dpii_f_chandle (i_c) !== i_c) $stop;
`ifndef VERILATOR
if (dpii_f_string (i_n) != i_n) $stop;
`ifndef VERILATOR
if (dpii_f_real (i_d) != i_d+1.5) $stop;
`endif
`ifndef NO_SHORTREAL
@ -148,16 +150,22 @@ module t ();
dpii_v_shortint (i_s,o_s); if (o_s !== ~i_s) $stop;
dpii_v_longint (i_l,o_l); if (o_l !== ~i_l) $stop;
dpii_v_chandle (i_c,o_c); if (o_c !== i_c) $stop;
`ifndef VERILATOR
`ifndef VCS // Strange link error
dpii_v_string (i_n,o_n); if (o_n != i_n) $stop;
`endif
`ifndef VERILATOR
dpii_v_real (i_d,o_d); if (o_d != i_d+1.5) $stop;
`endif
`ifndef NO_SHORTREAL
dpii_v_shortreal(i_f,o_f); if (o_f != i_f+1.5) $stop;
`endif
if (dpii_f_strlen ("")!=0) $stop;
if (dpii_f_strlen ("s")!=1) $stop;
if (dpii_f_strlen ("st")!=2) $stop;
if (dpii_f_strlen ("str")!=3) $stop;
if (dpii_f_strlen ("stri")!=4) $stop;
if (dpii_f_strlen ("string_l")!=8) $stop;
if (dpii_f_strlen ("string_len")!=10) $stop;
dpii_f_void();
dpii_t_void();
dpii_t_void_context();

View File

@ -15,6 +15,7 @@
#include <stdio.h>
#include <svdpi.h>
#include <cstring>
//======================================================================
@ -63,6 +64,8 @@ extern "C" {
extern int dpii_t_void_context ();
extern int dpii_t_int (int i, int *o);
extern int dpii_f_strlen (const char* i);
extern int dpii_fa_bit(int i);
}
#endif
@ -97,6 +100,8 @@ void dpii_v_string (const char* i, const char** o) { *o = i; }
void dpii_v_real (double i, double* o) { *o = i + 1.5; }
void dpii_v_shortreal(float i, float* o) { *o = i + 1.5; }
int dpii_f_strlen (const char* i) { return strlen(i); }
//======================================================================
void dpii_f_void () {}