DPI $display like sformat metacomment and $sformatf

This commit is contained in:
Wilson Snyder 2010-01-17 19:13:44 -05:00
parent 0d1de96dbc
commit 72b596efb3
18 changed files with 244 additions and 31 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,16 +1190,18 @@ 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
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;
} else {
}
}
tconnects[ppinnum].second = pinp;
ppinnum++;
}
}
while (ppinnum < tpinnum) {
nodep->v3error("Too few arguments in function call to "<<nodep->taskp()->prettyTypeName());

View File

@ -75,6 +75,7 @@
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);

View File

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

View File

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

32
test_regress/t/t_dpi_display.pl Executable file
View File

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

View File

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

View File

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