diff --git a/Changes b/Changes index 120b21fc1..81b619215 100644 --- a/Changes +++ b/Changes @@ -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] diff --git a/bin/verilator b/bin/verilator index 28f8e6094..7a3ff5564 100755 --- a/bin/verilator +++ b/bin/verilator @@ -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 diff --git a/include/verilated.cpp b/include/verilated.cpp index 65f23c7ea..8da6b0b16 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -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); diff --git a/include/verilated.h b/include/verilated.h index 95dccec0f..18f8a7e62 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -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); diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 66b05e274..573ec378f 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -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())); } } diff --git a/src/V3Ast.h b/src/V3Ast.h index 8a4dbcaf9..3a251d7f4 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -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 { diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index b20683d08..8f28a46f4 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -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 { diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index e69fdae4e..d7036e6c2 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -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()); } diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 23d8f0f0b..b99410f5e 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -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. diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index ec20f277c..3b47efef9 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -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()); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 29df8d5b2..b2c604bea 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -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()); } diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 1a625d132..a5fdff445 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -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*) { diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 529f228b6..a49c5d9f8 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -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."); } } diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp index b2949599a..af841c3d5 100644 --- a/src/V3Signed.cpp +++ b/src/V3Signed.cpp @@ -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 "<text()<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 diff --git a/src/verilog.l b/src/verilog.l index 1d9a7fd05..3fa2ae51d 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -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; } diff --git a/src/verilog.y b/src/verilog.y index c090ab505..dc502259d 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -355,10 +355,12 @@ class AstSenTree; %token yD_RANDOM "$random" %token yD_READMEMB "$readmemb" %token yD_READMEMH "$readmemh" +%token yD_SFORMAT "$sformat" %token yD_SIGNED "$signed" %token yD_SSCANF "$sscanf" %token yD_STIME "$stime" %token yD_STOP "$stop" +%token yD_SWRITE "$swrite" %token yD_TESTPLUSARGS "$test$plusargs" %token yD_TIME "$time" %token yD_UNIT "$unit" @@ -1926,6 +1928,9 @@ system_t_call: // 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); } diff --git a/test_regress/t/t_sys_sformat.pl b/test_regress/t/t_sys_sformat.pl new file mode 100755 index 000000000..7058e622f --- /dev/null +++ b/test_regress/t/t_sys_sformat.pl @@ -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; diff --git a/test_regress/t/t_sys_sformat.v b/test_regress/t/t_sys_sformat.v new file mode 100644 index 000000000..1d470cd20 --- /dev/null +++ b/test_regress/t/t_sys_sformat.v @@ -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 diff --git a/test_regress/t/t_sys_sformat_noopt.pl b/test_regress/t/t_sys_sformat_noopt.pl new file mode 100755 index 000000000..d069563a7 --- /dev/null +++ b/test_regress/t/t_sys_sformat_noopt.pl @@ -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;