diff --git a/bin/verilator b/bin/verilator index 9d9c4d3e8..0ef3c3db2 100755 --- a/bin/verilator +++ b/bin/verilator @@ -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. diff --git a/include/verilated.cpp b/include/verilated.cpp index 53f1b3999..b7f551ba9 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -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 diff --git a/include/verilated.h b/include/verilated.h index 6ebb3e83b..fc7747610 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -36,7 +36,7 @@ #include #include // avoided to reduce compile time -// avoided to reduce compile time +// avoided and instead in verilatedheavy.h to reduce compile time using namespace std; //========================================================================= diff --git a/include/verilatedheavy.h b/include/verilatedheavy.h new file mode 100644 index 000000000..65b0cc314 --- /dev/null +++ b/include/verilatedheavy.h @@ -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 + +//====================================================================== + +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 diff --git a/include/verilatedimp.h b/include/verilatedimp.h index 7e2c4afc4..0c70f6578 100644 --- a/include/verilatedimp.h +++ b/include/verilatedimp.h @@ -30,6 +30,7 @@ #include "verilatedos.h" #include "verilated.h" +#include "verilatedheavy.h" #include #include diff --git a/src/V3Ast.h b/src/V3Ast.h index 012dd1d6f..71879186b 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -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 { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 1c99b92ac..72383260b 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -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; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 952e784bc..d6fae4744 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -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) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index edd34fdbe..2a8afeea6 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1090,6 +1090,11 @@ private: nodep->v3error("Expecting expression to be constant, but variable isn't const: "<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)"); diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 980a19a8d..a453c29d9 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -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; sortnextp()) { 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"); } diff --git a/src/V3EmitCInlines.cpp b/src/V3EmitCInlines.cpp index 510267334..c4d24b686 100644 --- a/src/V3EmitCInlines.cpp +++ b/src/V3EmitCInlines.cpp @@ -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 via verilatedheavy.h when we create symbol file + } + } // NOPs virtual void visit(AstNodeStmt*, AstNUser*) {} diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 3e96cc15e..c4fe992cf 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -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()) { diff --git a/src/V3Global.h b/src/V3Global.h index 9c247b245..d632aaf46 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -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; } }; diff --git a/src/V3Link.cpp b/src/V3Link.cpp index d1fa8b7ca..3193173ca 100644 --- a/src/V3Link.cpp +++ b/src/V3Link.cpp @@ -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; diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp index 2c2f0dd4d..7984fdc7c 100644 --- a/src/V3Signed.cpp +++ b/src/V3Signed.cpp @@ -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 diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 7930de9b3..c1ee723b7 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -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); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 7b6ce8323..d6bb2c08e 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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()); + } } } } diff --git a/test_regress/t/t_dpi_import.v b/test_regress/t/t_dpi_import.v index e57488d37..85ffa091c 100644 --- a/test_regress/t/t_dpi_import.v +++ b/test_regress/t/t_dpi_import.v @@ -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(); diff --git a/test_regress/t/t_dpi_import_c.cpp b/test_regress/t/t_dpi_import_c.cpp index 6807bb358..1d04b12a7 100644 --- a/test_regress/t/t_dpi_import_c.cpp +++ b/test_regress/t/t_dpi_import_c.cpp @@ -15,6 +15,7 @@ #include #include +#include //====================================================================== @@ -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 () {}