From 72b596efb3b1f1ee43f84af7bbbea354858abbdf Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 17 Jan 2010 19:13:44 -0500 Subject: [PATCH] DPI $display like sformat metacomment and $sformatf --- bin/verilator | 24 +++++++++++--- include/verilated.cpp | 10 ++++++ include/verilatedheavy.h | 3 ++ src/V3Ast.h | 5 +-- src/V3AstNodes.h | 8 ++++- src/V3Clean.cpp | 3 +- src/V3EmitC.cpp | 6 ++++ src/V3EmitV.cpp | 3 ++ src/V3LinkParse.cpp | 5 +++ src/V3Number.cpp | 17 ++++++++++ src/V3Number.h | 1 + src/V3Task.cpp | 24 +++++++++----- src/V3Width.cpp | 51 +++++++++++++++++++++--------- src/verilog.l | 1 + src/verilog.y | 2 ++ test_regress/t/t_dpi_display.pl | 32 +++++++++++++++++++ test_regress/t/t_dpi_display.v | 38 ++++++++++++++++++++++ test_regress/t/t_dpi_display_c.cpp | 42 ++++++++++++++++++++++++ 18 files changed, 244 insertions(+), 31 deletions(-) create mode 100755 test_regress/t/t_dpi_display.pl create mode 100644 test_regress/t/t_dpi_display.v create mode 100644 test_regress/t/t_dpi_display_c.cpp diff --git a/bin/verilator b/bin/verilator index 0ef3c3db2..f9e477841 100755 --- a/bin/verilator +++ b/bin/verilator @@ -1227,6 +1227,15 @@ DPI compatible but is easier to read and better supports multiple designs. Vour::publicSetBool(value); // or top->publicSetBool(value); +Verilator also allows writing $display like functions using this syntax: + + import "DPI-C" function void + \$my_display (input string formatted /*verilator sformat*/ ); + +The /*verilator sformat*/ indicates that this function accepts a $display +like format specifier followed by any number of arguments to satisfy the +format. + Instead of DPI exporting, there's also Verilator public functions, which are slightly faster, but less compatible. @@ -1621,10 +1630,17 @@ using the --public switch. =item /*verilator sc_clock*/ -Used after a input declaration to indicate the signal should be declared in -SystemC as a sc_clock instead of a bool. This was needed in SystemC 1.1 -and 1.2 only; versions 2.0 and later do not require clock pins to be -sc_clocks and this is no longer needed. +Rarely needed. Used after a input declaration to indicate the signal +should be declared in SystemC as a sc_clock instead of a bool. This was +needed in SystemC 1.1 and 1.2 only; versions 2.0 and later do not require +clock pins to be sc_clocks and this is no longer needed. + +=item /*verilator sformat*/ + +Attached to the final input of a function or task "input string" to +indicate the function or task should pass all remaining arguments through +$sformatf. This allows creation of DPI functions with $display like +behavior. See the test_regress/t/t_dpi_display.v file for an example. =item /*verilator tracing_off*/ diff --git a/include/verilated.cpp b/include/verilated.cpp index b7f551ba9..1f96a9d3a 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -692,6 +692,16 @@ void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) { _VL_STRING_TO_VINT(obits, destp, output.length(), output.c_str()); } +string VL_SFORMATF_NX(const char* formatp, ...) { + va_list ap; + va_start(ap,formatp); + string output; + _vl_vsformat(output, formatp, ap); + va_end(ap); + + return output; +} + void VL_WRITEF(const char* formatp, ...) { va_list ap; va_start(ap,formatp); diff --git a/include/verilatedheavy.h b/include/verilatedheavy.h index 65b0cc314..5c12686fe 100644 --- a/include/verilatedheavy.h +++ b/include/verilatedheavy.h @@ -34,6 +34,7 @@ #include //====================================================================== +// Conversion functions extern string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp); inline string VL_CVT_PACK_STR_NQ(QData lhs) { @@ -45,4 +46,6 @@ inline string VL_CVT_PACK_STR_NI(IData lhs) { return VL_CVT_PACK_STR_NW(1, lw); } +extern string VL_SFORMATF_NX(const char* formatp, ...); + #endif // Guard diff --git a/src/V3Ast.h b/src/V3Ast.h index 4aea8df67..d3e39b239 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -179,14 +179,15 @@ public: VAR_CLOCK_ENABLE, // V3LinkParse moves to AstVar::attrClockEn VAR_PUBLIC, // V3LinkParse moves to AstVar::sigPublic VAR_PUBLIC_FLAT, // V3LinkParse moves to AstVar::sigPublic - VAR_ISOLATE_ASSIGNMENTS // V3LinkParse moves to AstVar::attrIsolateAssign + VAR_ISOLATE_ASSIGNMENTS, // V3LinkParse moves to AstVar::attrIsolateAssign + VAR_SFORMAT // V3LinkParse moves to AstVar::attrSFormat }; enum en m_e; const char* ascii() const { static const char* names[] = { "BITS", "VAR_BASE", "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT", - "VAR_ISOLATE_ASSIGNMENTS" + "VAR_ISOLATE_ASSIGNMENTS", "VAR_SFORMAT" }; return names[m_e]; }; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 0af2d0568..333414236 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -513,6 +513,7 @@ private: bool m_funcReturn:1; // Return variable for a function bool m_attrClockEn:1;// User clock enable attribute bool m_attrIsolateAssign:1;// User isolate_assignments attribute + bool m_attrSFormat:1;// User sformat attribute bool m_fileDescr:1; // File descriptor bool m_isConst:1; // Table contains constant data bool m_isStatic:1; // Static variable @@ -525,7 +526,7 @@ private: m_usedClock=false; m_usedParam=false; m_sigPublic=false; m_sigModPublic=false; m_funcLocal=false; m_funcReturn=false; - m_attrClockEn=false; m_attrIsolateAssign=false; + m_attrClockEn=false; m_attrIsolateAssign=false; m_attrSFormat=false; m_fileDescr=false; m_isConst=false; m_isStatic=false; m_trace=false; } @@ -584,6 +585,7 @@ public: void attrFileDescr(bool flag) { m_fileDescr = flag; } void attrScClocked(bool flag) { m_scClocked = flag; } void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; } + void attrSFormat(bool flag) { m_attrSFormat = flag; } void usedClock(bool flag) { m_usedClock = flag; } void usedParam(bool flag) { m_usedParam = flag; } void sigPublic(bool flag) { m_sigPublic = flag; } @@ -638,6 +640,7 @@ public: bool attrClockEn() const { return m_attrClockEn; } bool attrFileDescr() const { return m_fileDescr; } bool attrScClocked() const { return m_scClocked; } + bool attrSFormat() const { return m_attrSFormat; } bool attrIsolateAssign() const { return m_attrIsolateAssign; } uint32_t arrayElements() const; // 1, or total multiplication of all dimensions virtual string verilogKwd() const; @@ -1518,6 +1521,7 @@ public: virtual int instrCount() const { return instrCountPli(); } virtual V3Hash sameHash() const { return V3Hash(text()); } virtual bool same(AstNode* samep) const { return text()==samep->castSFormatF()->text(); } + virtual string verilogKwd() const { return "$sformatf"; } void exprsp(AstNode* nodep) { addOp1p(nodep); } // op1 = Expressions to output AstNode* exprsp() const { return op1p()->castNode(); } // op1 = Expressions to output string text() const { return m_text; } // * = Text to display @@ -1543,6 +1547,7 @@ public: } ASTNODE_NODE_FUNCS(Display, DISPLAY) virtual void dump(ostream& str); + virtual bool broken() const { return !fmtp(); } virtual string verilogKwd() const { return (filep() ? (string)"$f"+(string)displayType().ascii() : (string)"$"+(string)displayType().ascii()); } virtual bool isGateOptimizable() const { return false; } @@ -1572,6 +1577,7 @@ struct AstSFormat : public AstNode { setOp3p(lhsp); } ASTNODE_NODE_FUNCS(SFormat, SFORMAT) + virtual bool broken() const { return !fmtp(); } virtual string verilogKwd() const { return "$sformat"; } virtual string emitVerilog() { V3ERROR_NA; return ""; } virtual string emitC() { V3ERROR_NA; return ""; } diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 908f38d78..7124a8bff 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -95,7 +95,7 @@ private: CleanState clstate = getCleanState(nodep); if (clstate==CLEAN) return true; if (clstate==DIRTY) return false; - nodep->v3fatalSrc("Unknown clean state on node."); + nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName()); return false; } void setClean(AstNode* nodep, bool isClean) { @@ -243,6 +243,7 @@ private: virtual void visit(AstSFormatF* nodep, AstNUser*) { nodep->iterateChildren(*this); insureCleanAndNext (nodep->exprsp()); + setClean(nodep, true); // generates a string, so not relevant } virtual void visit(AstUCStmt* nodep, AstNUser*) { nodep->iterateChildren(*this); diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 3b8fb3988..b282f1ae3 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -217,6 +217,9 @@ public: virtual void visit(AstSFormat* nodep, AstNUser*) { displayNode(nodep, nodep->fmtp()->scopeNamep(), nodep->fmtp()->text(), nodep->fmtp()->exprsp(), false); } + virtual void visit(AstSFormatF* nodep, AstNUser*) { + displayNode(nodep, nodep->scopeNamep(), nodep->text(), nodep->exprsp(), false); + } virtual void visit(AstFScanF* nodep, AstNUser*) { displayNode(nodep, NULL, nodep->text(), nodep->exprsp(), true); } @@ -1079,6 +1082,9 @@ void EmitCStmts::displayEmit(AstNode* nodep, bool isScan) { putbs(","); dispp->lhsp()->iterate(*this); putbs(","); + } else if (AstSFormatF* dispp = nodep->castSFormatF()) { + if (dispp) {} + puts("VL_SFORMATF_NX("); } else { isStmt = true; nodep->v3fatalSrc("Unknown displayEmit node type"); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 6fa5b3628..b617b2292 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -207,6 +207,9 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { virtual void visit(AstSFormat* nodep, AstNUser*) { visitNodeDisplay(nodep, nodep->lhsp(), nodep->fmtp()->text(), nodep->fmtp()->exprsp()); } + virtual void visit(AstSFormatF* nodep, AstNUser*) { + visitNodeDisplay(nodep, NULL, nodep->text(), nodep->exprsp()); + } virtual void visit(AstValuePlusArgs* nodep, AstNUser*) { visitNodeDisplay(nodep, NULL, nodep->text(), nodep->exprsp()); } diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 999558fe4..3e5977fff 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -287,6 +287,11 @@ private: m_varp->attrIsolateAssign(true); nodep->unlinkFrBack()->deleteTree(); nodep=NULL; } + else if (nodep->attrType() == AstAttrType::VAR_SFORMAT) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->attrSFormat(true); + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } } virtual void visit(AstDefImplicitDType* nodep, AstNUser*) { diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 74ed64c11..ed199c216 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -517,6 +517,23 @@ vlsint64_t V3Number::toSQuad() const { return (vlsint64_t)(extended); } +string V3Number::toString() const { + UASSERT(!isFourState(),"toString with 4-state "<<*this); + // Spec says always drop leading zeros, this isn't quite right, we space pad. + int bit=this->width()-1; + bool start=true; + while ((bit%8)!=7) bit++; + string str; + for (; bit>=0; bit -= 8) { + int v = bitsValue(bit-7, 8); + if (!start || v) { + str += (char)((v==0)?' ':v); + start = false; // Drop leading 0s + } + } + return str; +} + uint32_t V3Number::toHash() const { return m_value[0]; } diff --git a/src/V3Number.h b/src/V3Number.h index 5e7782073..ad9778b2c 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -149,6 +149,7 @@ public: vlsint32_t toSInt() const; vluint64_t toUQuad() const; vlsint64_t toSQuad() const; + string toString() const; uint32_t toHash() const; uint32_t dataWord(int word) const; uint32_t countOnes() const; diff --git a/src/V3Task.cpp b/src/V3Task.cpp index c1ee723b7..7922c34f0 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1168,6 +1168,7 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp) // Find ports //map name_to_pinnum; int tpinnum = 0; // Note grammar starts pin counting at one + AstVar* sformatp = NULL; for (AstNode* stmtp = taskStmtsp; stmtp; stmtp=stmtp->nextp()) { if (AstVar* portp = stmtp->castVar()) { if (portp->isIO()) { @@ -1176,6 +1177,11 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp) // That'll require a AstTpin or somesuch which will replace the ppinnum counting //name_to_pinnum.insert(make_pair(portp->name(), tpinnum)); tpinnum++; + if (portp->attrSFormat()) { + sformatp = portp; + } else if (sformatp) { + nodep->v3error("/*verilator sformat*/ can only be applied to last argument of a function"); + } } } } @@ -1184,15 +1190,17 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp) int ppinnum = 0; for (AstNode* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()) { if (ppinnum >= tpinnum) { - // Use v3warn so we'll only get the error once for each function - pinp->v3error("Too many arguments in function call to "<taskp()->prettyTypeName()); - // We'll just delete them; seems less error prone than making a false argument - pinp->unlinkFrBackWithNext()->deleteTree(); pinp=NULL; - break; - } else { - tconnects[ppinnum].second = pinp; - ppinnum++; + if (sformatp) { + tconnects.push_back(make_pair(sformatp, (AstNode*)NULL)); + } else { + pinp->v3error("Too many arguments in function call to "<taskp()->prettyTypeName()); + // We'll just delete them; seems less error prone than making a false argument + pinp->unlinkFrBackWithNext()->deleteTree(); pinp=NULL; + break; + } } + tconnects[ppinnum].second = pinp; + ppinnum++; } while (ppinnum < tpinnum) { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 988da5001..cd075210f 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -60,21 +60,22 @@ //###################################################################### // Width state, as a visitor of each AstNode - enum Stage { PRELIM=1,FINAL=2,BOTH=3 }; +enum Stage { PRELIM=1,FINAL=2,BOTH=3 }; - class WidthVP : public AstNUser { - // Parameters to pass down hierarchy with visit functions. - int m_width; // Expression width, for (2+2), it's 32 bits - int m_minWidth; // Minimum width, for (2+2), it's 2 bits, for 32'2+32'2 it's 32 bits - Stage m_stage; // If true, report errors - public: - WidthVP(int width, int minWidth, Stage stage) : m_width(width), m_minWidth(minWidth), m_stage(stage) {} - int width() const { return m_width; } - int widthMin() const { return m_minWidth?m_minWidth:m_width; } - bool prelim() const { return m_stage&1; } - bool final() const { return m_stage&2; } - }; +class WidthVP : public AstNUser { + // Parameters to pass down hierarchy with visit functions. + int m_width; // Expression width, for (2+2), it's 32 bits + int m_minWidth; // Minimum width, for (2+2), it's 2 bits, for 32'2+32'2 it's 32 bits + Stage m_stage; // If true, report errors +public: + WidthVP(int width, int minWidth, Stage stage) : m_width(width), m_minWidth(minWidth), m_stage(stage) {} + int width() const { return m_width; } + int widthMin() const { return m_minWidth?m_minWidth:m_width; } + bool prelim() const { return m_stage&1; } + bool final() const { return m_stage&2; } +}; +//###################################################################### class WidthVisitor : public AstNVisitor { private: @@ -943,16 +944,36 @@ private: // And do the arguments to the task/function too for (int accept_mode=1; accept_mode>=0; accept_mode--) { // Avoid duplicate code; just do inner stuff twice V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp()); - for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) { + bool lastloop = false; + for (V3TaskConnects::iterator it=tconnects.begin(); !lastloop && it!=tconnects.end(); ++it) { AstVar* portp = it->first; AstNode* pinp = it->second; if (pinp!=NULL) { // Else argument error we'll find later 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 + if (portp->attrSFormat() + && (!pinp->castSFormatF() || pinp->nextp())) { // Not already done + UINFO(4," sformat via metacomment: "<unlinkFrBackWithNext(&handle); // Format + additional args, if any + AstNode* argsp = NULL; + if (pinp->nextp()) argsp = pinp->nextp()->unlinkFrBackWithNext(); + string format; + if (pinp->castConst()) format = pinp->castConst()->num().toString(); + else pinp->v3error("Format to $display-like function must have constant format string"); + AstSFormatF* newp = new AstSFormatF(nodep->fileline(), format, argsp); + if (!newp->scopeNamep() && newp->formatScopeTracking()) { + newp->scopeNamep(new AstScopeName(newp->fileline())); + } + handle.relink(newp); + // Connection list is now incorrect (has extra args in it). + lastloop = true; // so exit early; next loop will correct it + } + else if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::STRING && !pinp->castCvtPackString() && !(pinp->castVarRef() && pinp->castVarRef()->varp()->basicp()->keyword()==AstBasicDTypeKwd::STRING)) { + UINFO(4," Add CvtPackString: "<unlinkFrBack(&handle); // No next, that's the next pin AstNode* newp = new AstCvtPackString(pinp->fileline(), pinp); diff --git a/src/verilog.l b/src/verilog.l index d0b66d22c..6700f56ac 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -589,6 +589,7 @@ escid \\[^ \t\f\r\n]+ "/*verilator public_flat*/" { FL; return yVL_PUBLIC_FLAT; } "/*verilator public_module*/" { FL; return yVL_PUBLIC_MODULE; } "/*verilator sc_clock*/" { FL; return yVL_CLOCK; } + "/*verilator sformat*/" { FL; return yVL_SFORMAT; } "/*verilator systemc_clock*/" { FL; return yVL_CLOCK; } "/*verilator tracing_off*/" {PARSEP->fileline()->tracingOn(false); } "/*verilator tracing_on*/" {PARSEP->fileline()->tracingOn(true); } diff --git a/src/verilog.y b/src/verilog.y index 0ef58858d..2f486f109 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -416,6 +416,7 @@ class AstSenTree; %token yVL_ISOLATE_ASSIGNMENTS "/*verilator isolate_assignments*/" %token yVL_NO_INLINE_MODULE "/*verilator no_inline_module*/" %token yVL_NO_INLINE_TASK "/*verilator no_inline_task*/" +%token yVL_SFORMAT "/*verilator sformat*/" %token yVL_PARALLEL_CASE "/*verilator parallel_case*/" %token yVL_PUBLIC "/*verilator public*/" %token yVL_PUBLIC_FLAT "/*verilator public_flat*/" @@ -1525,6 +1526,7 @@ sigAttr: | yVL_PUBLIC { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC); } | yVL_PUBLIC_FLAT { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT); } | yVL_ISOLATE_ASSIGNMENTS { $$ = new AstAttrOf($1,AstAttrType::VAR_ISOLATE_ASSIGNMENTS); } + | yVL_SFORMAT { $$ = new AstAttrOf($1,AstAttrType::VAR_SFORMAT); } ; rangeListE: // IEEE: [{packed_dimension}] diff --git a/test_regress/t/t_dpi_display.pl b/test_regress/t/t_dpi_display.pl new file mode 100755 index 000000000..61d536338 --- /dev/null +++ b/test_regress/t/t_dpi_display.pl @@ -0,0 +1,32 @@ +#!/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_display_c.cpp"], + ); + +execute ( + check_finished=>1, + expect=>quotemeta( +q{dpii_display_call: +dpii_display_call: c +dpii_display_call: co +dpii_display_call: cons +dpii_display_call: constant +dpii_display_call: constant_value +one10=0000000a +dpii_display_call: one10=0000000a +Mod=top.v 16= 10 10=0000000a +dpii_display_call: Mod=top.v 16= 10 10=0000000a +*-* All Finished *-* +}), + ); + +ok(1); +1; diff --git a/test_regress/t/t_dpi_display.v b/test_regress/t/t_dpi_display.v new file mode 100644 index 000000000..a307130e1 --- /dev/null +++ b/test_regress/t/t_dpi_display.v @@ -0,0 +1,38 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2010 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. + +module t (); + +`ifndef VERILATOR + `error "Only Verilator supports PLI-ish DPI calls and sformat conversion." +`endif + + import "DPI-C" context dpii_display_call + = function void \$dpii_display (input string formatted /*verilator sformat*/ ); + + integer a; + + initial begin + // Check variable width constant string conversions + $dpii_display(""); + $dpii_display("c"); + $dpii_display("co"); + $dpii_display("cons"); + $dpii_display("constant"); + $dpii_display("constant_value"); + + a = $c("10"); // Don't optimize away "a" + $display ("one10=%x ",a); // Check single arg + $dpii_display("one10=%x ",a); + $display ("Mod=%m 16=%d 10=%x ",a,a); // Check multiarg + $dpii_display("Mod=%m 16=%d 10=%x ",a,a); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_dpi_display_c.cpp b/test_regress/t/t_dpi_display_c.cpp new file mode 100644 index 000000000..8acb8d45f --- /dev/null +++ b/test_regress/t/t_dpi_display_c.cpp @@ -0,0 +1,42 @@ +// -*- C++ -*- +//************************************************************************* +// +// Copyright 2009-2010 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License. +// Version 2.0. +// +// Verilator is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +//************************************************************************* + +#include +#include + +//====================================================================== + +#if defined(VERILATOR) +# include "Vt_dpi_display__Dpi.h" +#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 void dpii_display_call (const char* c); +} +#endif + +//====================================================================== + +void dpii_display_call(const char* c) { + VL_PRINTF("dpii_display_call: %s\n", c); +}