Support and .

This commit is contained in:
Wilson Snyder 2009-11-23 21:24:55 -05:00
parent d608fd77b9
commit c7d8eb126f
21 changed files with 206 additions and 44 deletions

View File

@ -18,6 +18,8 @@ indicates the contributor was also the author of the fix; Thanks!
*** Support $test$plusargs and $value$plusargs, but see the docs!
*** Support $sformat and $swrite.
*** Add VARHIDDEN warning when signal name hides module name.
**** Fix MinGW compilation, bug184. [by Shankar Giri]

View File

@ -1282,7 +1282,7 @@ output. [signal_32_bits = $c32("...");] This allows for compatibility with
other simulators which require a differently named PLI function name for
each different output width.
=item $display, $write, $fdisplay, $fwrite
=item $display, $write, $fdisplay, $fwrite, $sformat, $swrite
Format arguments may use C fprintf sizes after the % escape. Per the
Verilog standard, %x prints a number with the natural width, %0x prints a
@ -1730,10 +1730,11 @@ $unsigned, $warning.
Generally supported.
=item $display, $write, $fdisplay, $fwrite
=item $display, $write, $fdisplay, $fwrite, $swrite
$display and friends must have a constant format string as the first
argument (as with C's printf), you cannot simply list variables standalone.
argument (as with C's printf). The rare usage which lists variables
standalone without a format is not supported.
=item $displayb, $displayh, $displayo, $writeb, $writeh, $writeo, etc

View File

@ -698,6 +698,16 @@ QData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode) {
return VL_CVT_FP_Q(fopen(filenamez,modez));
}
void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...) {
va_list ap;
va_start(ap,formatp);
string output;
_vl_vsformat(output, formatp, ap);
va_end(ap);
_VL_STRING_TO_VINT(obits, destp, output.length(), output.c_str());
}
void VL_WRITEF(const char* formatp, ...) {
va_list ap;
va_start(ap,formatp);

View File

@ -199,7 +199,7 @@ extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp); ///< Zero reset a
extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool is_modulus);
/// File I/O
extern IData VL_FGETS_IXQ(int sbits, void* strgp, QData fpq);
extern IData VL_FGETS_IXQ(int obits, void* destp, QData fpq);
extern QData VL_FOPEN_WI(int fnwords, WDataInP ofilename, IData mode);
extern QData VL_FOPEN_QI(QData ofilename, IData mode);
@ -221,6 +221,8 @@ extern IData VL_SSCANF_IIX(int lbits, IData ld, const char* formatp, ...);
extern IData VL_SSCANF_IQX(int lbits, QData ld, const char* formatp, ...);
extern IData VL_SSCANF_IWX(int lbits, WDataInP lwp, const char* formatp, ...);
extern void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...);
extern IData VL_TESTPLUSARGS_I(const char* formatp);
extern IData VL_VALUEPLUSARGS_IW(int rbits, const char* prefixp, char fmt, WDataOutP rwp);

View File

@ -65,9 +65,7 @@ private:
AstNode* timesp = nodep->exprsp(); if (timesp) timesp->unlinkFrBack();
timesp = timesp->addNext(new AstTime(nodep->fileline()));
nodep->exprsp(timesp);
if (!nodep->scopeNamep()
&& (nodep->name().find("%m") != string::npos
|| nodep->name().find("%M") != string::npos)) {
if (!nodep->scopeNamep() && nodep->formatScopeTracking()) {
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
}
}

View File

@ -1164,20 +1164,24 @@ public:
void iterateChildren(AstNVisitor& v, AstNUser* vup=NULL) { }
};
class AstNodePli : public AstNodeStmt {
class AstNodeDisplay : public AstNodeStmt {
// AstDisplay, AstSFormat or anything else with a format that might have %m
string m_text;
public:
AstNodePli(FileLine* fl, const string& text, AstNode* exprsp)
AstNodeDisplay(FileLine* fl, const string& text, AstNode* exprsp)
: AstNodeStmt(fl), m_text(text) {
addNOp1p(exprsp); }
ASTNODE_BASE_FUNCS(NodePli)
addNOp1p(exprsp); addNOp2p(NULL); }
ASTNODE_BASE_FUNCS(NodeDisplay)
virtual string name() const { return m_text; }
virtual int instrCount() const { return instrCountPli(); }
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
void text(const string& text) { m_text=text; }
// op2p,op3p... used by AstDisplay
AstScopeName* scopeNamep() const { return op2p()->castScopeName(); }
void scopeNamep(AstNode* nodep) { setNOp2p(nodep); }
bool formatScopeTracking() const { // Track scopeNamep(); Ok if over-eager
return (name().find("%m") != string::npos || name().find("%M") != string::npos); }
};
struct AstNodeText : public AstNode {

View File

@ -1382,15 +1382,15 @@ public:
void ignoreOverlap(bool flag) { m_ignoreOverlap = flag; }
};
struct AstDisplay : public AstNodePli {
struct AstDisplay : public AstNodeDisplay {
// Parents: stmtlist
// Children: file which must be a varref, MATH to print
private:
AstDisplayType m_displayType;
public:
AstDisplay(FileLine* fileline, AstDisplayType dispType, const string& text, AstNode* filep, AstNode* exprsp)
: AstNodePli (fileline, text, exprsp) {
setNOp2p(filep);
: AstNodeDisplay (fileline, text, exprsp) {
setNOp3p(filep);
m_displayType = dispType;
}
ASTNODE_NODE_FUNCS(Display, DISPLAY)
@ -1406,14 +1406,37 @@ public:
virtual bool same(AstNode* samep) const {
return displayType()==samep->castDisplay()->displayType()
&& text()==samep->castDisplay()->text(); }
// op1 used by AstNodePli
// op1,op2 used by AstNodeDisplay
AstDisplayType displayType() const { return m_displayType; }
void displayType(AstDisplayType type) { m_displayType = type; }
bool addNewline() const { return displayType().addNewline(); } // * = Add a newline for $display
AstNode* filep() const { return op2p(); }
void filep(AstNodeVarRef* nodep) { setNOp2p(nodep); }
AstScopeName* scopeNamep() const { return op3p()->castScopeName(); }
void scopeNamep(AstNode* nodep) { setNOp3p(nodep); }
AstNode* filep() const { return op3p(); }
void filep(AstNodeVarRef* nodep) { setNOp3p(nodep); }
};
struct AstSFormat : public AstNodeDisplay {
// Parents: statement container
// Children: string to load
// Children: varrefs to print
AstSFormat(FileLine* fileline, AstNode* lhsp, const string& text, AstNode* exprsp)
: AstNodeDisplay (fileline, text, exprsp) {
setOp3p(lhsp);
}
ASTNODE_NODE_FUNCS(SFormat, SFORMAT)
virtual string verilogKwd() const { return "$sformat"; }
virtual string emitVerilog() { V3ERROR_NA; return ""; }
virtual string emitC() { V3ERROR_NA; return ""; }
virtual bool isGateOptimizable() const { return false; }
virtual bool isPredictOptimizable() const { return true; }
virtual bool isSplittable() const { return true; }
virtual bool isOutputter() const { return false; }
virtual bool cleanOut() { return false; }
virtual V3Hash sameHash() const { return V3Hash(text()); }
virtual bool same(AstNode* samep) const {
return text()==samep->castSFormat()->text(); }
// op1,op2 used by AstNodeDisplay
AstNode* lhsp() const { return op3p(); }
void lhsp(AstNode* nodep) { setOp3p(nodep); }
};
struct AstFClose : public AstNodeStmt {

View File

@ -240,7 +240,7 @@ private:
nodep->iterateChildren(*this);
insureClean(nodep->condp());
}
virtual void visit(AstNodePli* nodep, AstNUser*) {
virtual void visit(AstNodeDisplay* nodep, AstNUser*) {
nodep->iterateChildren(*this);
insureCleanAndNext (nodep->exprsp());
}

View File

@ -1380,7 +1380,7 @@ private:
}
}
virtual void visit(AstDisplay* nodep, AstNUser*) {
virtual void visit(AstNodeDisplay* nodep, AstNUser*) {
// Substitute constants into displays. The main point of this is to
// simplify assertion methodologies which call functions with display's.
// This eliminates a pile of wide temps, and makes the C a whole lot more readable.

View File

@ -114,9 +114,9 @@ public:
putbs("VL_ASSIGNBIT_");
emitIQW(selp->fromp());
if (nodep->rhsp()->isAllOnesV()) {
putbs("O(");
puts("O(");
} else {
putbs("I(");
puts("I(");
}
puts(cvtToStr(nodep->widthMin())+",");
selp->lsbp()->iterateAndNext(*this); puts(", ");
@ -124,7 +124,7 @@ public:
} else {
putbs("VL_ASSIGNSEL_");
emitIQW (selp->fromp());
putbs("II");
puts("II");
emitIQW(nodep->rhsp());
puts("(");
puts(cvtToStr(nodep->widthMin())+",");
@ -230,6 +230,9 @@ public:
if (nodep->addNewline()) text += "\n";
displayNode(nodep, text, nodep->exprsp(), false);
}
virtual void visit(AstSFormat* nodep, AstNUser*) {
displayNode(nodep, nodep->text(), nodep->exprsp(), false);
}
virtual void visit(AstFScanF* nodep, AstNUser*) {
displayNode(nodep, nodep->text(), nodep->exprsp(), true);
}
@ -278,9 +281,9 @@ public:
puts(cvtToStr(nodep->exprsp()->widthMin())); // Note argument width, not node width (which is always 32)
putbs(",");
putsQuoted(prefix);
putbs(",'");
puts(cvtToStr(format));
putbs("',");
putbs(",");
puts("'"); puts(cvtToStr(format)); puts("'");
puts(",");
nodep->exprsp()->iterateAndNext(*this);
puts(")");
}
@ -1050,6 +1053,13 @@ void EmitCStmts::displayEmit(AstNode* nodep, bool isScan) {
} else {
puts("VL_WRITEF(");
}
} else if (AstSFormat* dispp = nodep->castSFormat()) {
isStmt = true;
puts("VL_SFORMAT_X(");
puts(cvtToStr(dispp->lhsp()->widthMin()));
putbs(",");
dispp->lhsp()->iterate(*this);
putbs(",");
} else {
isStmt = true;
nodep->v3fatalSrc("Unknown displayEmit node type");
@ -1165,8 +1175,8 @@ void EmitCStmts::displayNode(AstNode* nodep, const string& vformat, AstNode* exp
case 'm': {
emitDispState.pushFormat("%S");
emitDispState.pushArg(NULL, "vlSymsp->name()");
if (!nodep->castDisplay()) nodep->v3fatalSrc("Non-Display with %m");
AstScopeName* scopenamep = nodep->castDisplay()->scopeNamep();
if (!nodep->castNodeDisplay()) nodep->v3fatalSrc("Non-Display with %m");
AstScopeName* scopenamep = nodep->castNodeDisplay()->scopeNamep();
if (!scopenamep) nodep->v3fatalSrc("Display with %m but no AstScopeName");
for (AstText* textp=scopenamep->scopeAttrp(); textp; textp=textp->nextp()->castText()) {
emitDispState.pushFormat(textp->text());

View File

@ -172,10 +172,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
virtual void visit(AstCoverInc*, AstNUser*) {} // N/A
virtual void visit(AstCoverToggle*, AstNUser*) {} // N/A
void visitNodeDisplay(AstNode* nodep, AstNode* filep, const string& text, AstNode* exprsp) {
void visitNodeDisplay(AstNode* nodep, AstNode* fileOrStrgp, const string& text, AstNode* exprsp) {
putbs(nodep->verilogKwd());
putbs(" (");
if (filep) { filep->iterateAndNext(*this); putbs(","); }
if (fileOrStrgp) { fileOrStrgp->iterateAndNext(*this); putbs(","); }
puts("\"");
putsNoTracking(text); // Not putsQuoted, as display text contains \ already
puts("\"");
@ -194,6 +194,9 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
virtual void visit(AstSScanF* nodep, AstNUser*) {
visitNodeDisplay(nodep, nodep->fromp(), nodep->text(), nodep->exprsp());
}
virtual void visit(AstSFormat* nodep, AstNUser*) {
visitNodeDisplay(nodep, nodep->lhsp(), nodep->text(), nodep->exprsp());
}
virtual void visit(AstValuePlusArgs* nodep, AstNUser*) {
visitNodeDisplay(nodep, NULL, nodep->text(), nodep->exprsp());
}

View File

@ -174,6 +174,16 @@ private:
}
m_setRefLvalue = last_setRefLvalue;
}
virtual void visit(AstSFormat* nodep, AstNUser*) {
bool last_setRefLvalue = m_setRefLvalue;
{
m_setRefLvalue = true;
nodep->lhsp()->iterateAndNext(*this);
m_setRefLvalue = false;
nodep->exprsp()->iterateAndNext(*this);
}
m_setRefLvalue = last_setRefLvalue;
}
// Nodes that change LValue state
virtual void visit(AstSel* nodep, AstNUser*) {

View File

@ -302,21 +302,29 @@ private:
nodep->iterateChildren(*this);
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
}
virtual void visit(AstDisplay* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (nodep->filep()) expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
void expectNodeDisplay(AstNodeDisplay* nodep) {
// AstSFormat also handled here
expectFormat(nodep, nodep->text(), nodep->exprsp(), false);
if ((nodep->castDisplay() && nodep->castDisplay()->displayType().needScopeTracking())
|| nodep->formatScopeTracking()) {
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
}
}
virtual void visit(AstNodeDisplay* nodep, AstNUser*) {
nodep->iterateChildren(*this);
expectNodeDisplay(nodep);
}
virtual void visit(AstDisplay* nodep, AstNUser* vup) {
nodep->iterateChildren(*this);
expectNodeDisplay(nodep);
if (nodep->filep()) expectDescriptor(nodep, nodep->filep()->castNodeVarRef());
if (!m_assertp
&& (nodep->displayType() == AstDisplayType::INFO
|| nodep->displayType() == AstDisplayType::WARNING
|| nodep->displayType() == AstDisplayType::ERROR
|| nodep->displayType() == AstDisplayType::FATAL)) {
nodep->v3error(nodep->verilogKwd()+" only allowed under a assertion.");
}
if (nodep->displayType().needScopeTracking()
|| nodep->name().find("%m") != string::npos
|| nodep->name().find("%M") != string::npos) {
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
nodep->v3error(nodep->verilogKwd()+" only allowed under an assertion.");
}
}

View File

@ -192,7 +192,7 @@ private:
}
// VISITORS - Special
virtual void visit(AstDisplay* nodep, AstNUser*) {
virtual void visit(AstNodeDisplay* nodep, AstNUser*) {
nodep->iterateChildren(*this);
//
UINFO(9," Display in "<<nodep->text()<<endl);

View File

@ -545,7 +545,7 @@ private:
// default
// These types are definately not reducable
// AstCoverInc, AstNodePli, AstArraySel, AstStop, AstFinish,
// AstCoverInc, AstDisplay, AstArraySel, AstStop, AstFinish,
// AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt
virtual void visit(AstNode* nodep, AstNUser*) {
badNodeType(nodep);

View File

@ -682,7 +682,7 @@ private:
widthCheck(nodep,"Assign RHS",nodep->rhsp(),awidth,awidth);
//if (debug()) nodep->dumpTree(cout," AssignOut: ");
}
virtual void visit(AstNodePli* nodep, AstNUser*) {
virtual void visit(AstNodeDisplay* nodep, AstNUser*) {
// Excludes NodeDisplay, see below
// TOP LEVEL NODE
// Just let all arguments seek their natural sizes

View File

@ -180,10 +180,12 @@ escid \\[^ \t\f\r\n]+
"$removal" { FL; return yaTIMINGSPEC; }
"$setup" { FL; return yaTIMINGSPEC; }
"$setuphold" { FL; return yaTIMINGSPEC; }
"$sformat" { FL; return yD_SFORMAT; }
"$skew" { FL; return yaTIMINGSPEC; }
"$sscanf" { FL; return yD_SSCANF; }
"$stime" { FL; return yD_STIME; }
"$stop" { FL; return yD_STOP; }
"$swrite" { FL; return yD_SWRITE; }
"$test$plusargs" { FL; return yD_TESTPLUSARGS; }
"$time" { FL; return yD_TIME; }
"$timeskew" { FL; return yaTIMINGSPEC; }

View File

@ -355,10 +355,12 @@ class AstSenTree;
%token<fl> yD_RANDOM "$random"
%token<fl> yD_READMEMB "$readmemb"
%token<fl> yD_READMEMH "$readmemh"
%token<fl> yD_SFORMAT "$sformat"
%token<fl> yD_SIGNED "$signed"
%token<fl> yD_SSCANF "$sscanf"
%token<fl> yD_STIME "$stime"
%token<fl> yD_STOP "$stop"
%token<fl> yD_SWRITE "$swrite"
%token<fl> yD_TESTPLUSARGS "$test$plusargs"
%token<fl> yD_TIME "$time"
%token<fl> yD_UNIT "$unit"
@ -1926,6 +1928,9 @@ system_t_call<nodep>: // IEEE: system_tf_call (as task)
| yD_STOP parenE { $$ = new AstStop($1); }
| yD_STOP '(' expr ')' { $$ = new AstStop($1); }
//
| yD_SFORMAT '(' expr ',' str commaVRDListE ')' { $$ = new AstSFormat($1,$3,*$5,$6); }
| yD_SWRITE '(' expr ',' str commaVRDListE ')' { $$ = new AstSFormat($1,$3,*$5,$6); }
//
| yD_DISPLAY parenE { $$ = new AstDisplay($1,AstDisplayType::DISPLAY,"", NULL,NULL); }
| yD_DISPLAY '(' str commaEListE ')' { $$ = new AstDisplay($1,AstDisplayType::DISPLAY,*$3,NULL,$4); }
| yD_WRITE '(' str commaEListE ')' { $$ = new AstDisplay($1,AstDisplayType::WRITE, *$3,NULL,$4); }

18
test_regress/t/t_sys_sformat.pl Executable file
View File

@ -0,0 +1,18 @@
#!/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 (
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,44 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2008 by Wilson Snyder.
`include "verilated.v"
module t;
// Note $sscanf already tested elsewhere
reg [3:0] n;
reg [63:0] q;
reg [16*8:1] wide;
reg [48*8:1] str;
reg [48*8:1] str2;
initial begin
n = 4'b1100;
q = 64'h1234_5678_abcd_0123;
wide = "hello-there12345";
$sformat(str, "n=%b q=%d w=%s", n, q, wide);
`ifdef TEST_VERBOSE $display("str=%0s",str); `endif
if (str !== "n=1100 q= 1311768467750060323 w=hello-there12345") $stop;
q = {q[62:0],1'b1};
$swrite(str2, "n=%b q=%d w=%s", n, q, wide);
`ifdef TEST_VERBOSE $display("str2=%0s",str2); `endif
if (str2 !== "n=1100 q= 2623536935500120647 w=hello-there12345") $stop;
$swrite(str2, "mod=%m");
`ifdef TEST_VERBOSE $display("str2=%0s",str2); `endif
`ifdef verilator
if (str2 !== "mod=TOP.v") $stop;
`else
if (str2 !== "mod=top.t") $stop;
`endif
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,22 @@
#!/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.
top_filename("t/t_sys_sformat.v");
compile (
# Avoid inlining our simple example, to make sure verilated.h works right
verilator_flags2 => ["-O0"],
);
execute (
check_finished=>1,
);
ok(1);
1;