DPI $display like sformat metacomment and $sformatf
This commit is contained in:
parent
0d1de96dbc
commit
72b596efb3
|
|
@ -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*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include <string>
|
||||
|
||||
//======================================================================
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 ""; }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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*) {
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1168,6 +1168,7 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp)
|
|||
// Find ports
|
||||
//map<string,int> 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 "<<nodep->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 "<<nodep->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) {
|
||||
|
|
|
|||
|
|
@ -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: "<<nodep<<endl);
|
||||
AstNRelinker handle;
|
||||
pinp->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: "<<pinp<<endl);
|
||||
AstNRelinker handle;
|
||||
pinp->unlinkFrBack(&handle); // No next, that's the next pin
|
||||
AstNode* newp = new AstCvtPackString(pinp->fileline(), pinp);
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -416,6 +416,7 @@ class AstSenTree;
|
|||
%token<fl> yVL_ISOLATE_ASSIGNMENTS "/*verilator isolate_assignments*/"
|
||||
%token<fl> yVL_NO_INLINE_MODULE "/*verilator no_inline_module*/"
|
||||
%token<fl> yVL_NO_INLINE_TASK "/*verilator no_inline_task*/"
|
||||
%token<fl> yVL_SFORMAT "/*verilator sformat*/"
|
||||
%token<fl> yVL_PARALLEL_CASE "/*verilator parallel_case*/"
|
||||
%token<fl> yVL_PUBLIC "/*verilator public*/"
|
||||
%token<fl> yVL_PUBLIC_FLAT "/*verilator public_flat*/"
|
||||
|
|
@ -1525,6 +1526,7 @@ sigAttr<nodep>:
|
|||
| 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<rangep>: // IEEE: [{packed_dimension}]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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 <stdio.h>
|
||||
#include <svdpi.h>
|
||||
|
||||
//======================================================================
|
||||
|
||||
#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);
|
||||
}
|
||||
Loading…
Reference in New Issue