diff --git a/Changes b/Changes index 16d2b4348..3244204f5 100644 --- a/Changes +++ b/Changes @@ -7,6 +7,9 @@ indicates the contributor was also the author of the fix; Thanks! *** Add --top-module option to select between multiple tops. [Stefan Thiede] +**** Fix SystemVerilog parameterized defines with `` expansion, + and fix extra whitespace inserted on substitution. [Vladimir Matveyenko] + **** Fix no-module include files on command line. [Stefan Thiede] **** Fix dropping of backslash quoted-quote at end of $display. diff --git a/src/V3PreLex.h b/src/V3PreLex.h index 57cfb6e8b..3ec6d3843 100644 --- a/src/V3PreLex.h +++ b/src/V3PreLex.h @@ -133,9 +133,9 @@ class V3PreLex { void lineDirective(const char* text); void incLineno() { m_curFilelinep->incLineno(); } // Called by V3PreProc.cpp to inform lexer - void setStateDefArg(); - void setStateDefValue(); - void setStateIncFilename(); + void pushStateDefArg(); + void pushStateDefValue(); + void pushStateIncFilename(); void unputString(const char* textp); }; diff --git a/src/V3PreLex.l b/src/V3PreLex.l index 8309b0e58..3fd0953c3 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -140,14 +140,25 @@ psl [p]sl <> { yyerror("EOF in define argument list\n"); yyleng = 0; yyterminate(); } {crnl} { linenoInc(); yytext="\n"; yyleng=1; return(VP_WHITE); } {quote} { yy_push_state(STRMODE); yymore(); } -[(] { V3PreLex::s_currentLexp->m_parenLevel++; appendDefValue(yytext,yyleng); } -[,)] { if (V3PreLex::s_currentLexp->m_parenLevel>1) { +[(] { V3PreLex::s_currentLexp->m_parenLevel++; + if (V3PreLex::s_currentLexp->m_parenLevel>1) { appendDefValue(yytext,yyleng); - if (yytext[0]==')') V3PreLex::s_currentLexp->m_parenLevel--; } else { - unput(yytext[0]); yy_pop_state(); return (VP_DEFARG); + return (VP_TEXT); }} -[^\/\*\n\r\\(,)\"]+ | +[)] { V3PreLex::s_currentLexp->m_parenLevel--; + if (V3PreLex::s_currentLexp->m_parenLevel>0) { + appendDefValue(yytext,yyleng); + } else { + yy_pop_state(); return (VP_DEFARG); + }} +[,] { if (V3PreLex::s_currentLexp->m_parenLevel>1) { + appendDefValue(yytext,yyleng); + } else { + yy_pop_state(); return (VP_DEFARG); + }} +"`"{symb} { return (VP_DEFREF); } /* defref in defref */ +[^\/\*\n\r\\(,)\"`]+ | . { appendDefValue(yytext,yyleng); } /* One line comments. */ @@ -199,21 +210,21 @@ psl [p]sl . { return (VP_TEXT); } %% -void V3PreLex::setStateDefArg() { +void V3PreLex::pushStateDefArg() { // Enter define substitution argument state yy_push_state(ARGMODE); - m_parenLevel = 1; + m_parenLevel = 0; m_defValue = ""; } -void V3PreLex::setStateDefValue() { +void V3PreLex::pushStateDefValue() { // Enter define value state yy_push_state(DEFMODE); m_parenLevel = 0; m_defValue = ""; } -void V3PreLex::setStateIncFilename() { +void V3PreLex::pushStateIncFilename() { // Enter include <> filename state yy_push_state(INCMODE); yymore(); diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index a2130a850..6eb6bbc5b 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -61,6 +61,28 @@ public: string params() const { return m_params; } }; +//************************************************************************* + +class V3DefineRef { + // One for each pending define substitution + string m_name; // Define last name being defined + string m_params; // Define parameter list for next expansion + string m_nextarg; // String being built for next argument + int m_parenLevel; // Parenthesis counting inside def args + + vector m_args; // List of define arguments +public: + string name() const { return m_name; } + string params() const { return m_params; } + string nextarg() const { return m_nextarg; } + void nextarg(const string& value) { m_nextarg = value; } + int parenLevel() const { return m_parenLevel; } + vector& args() { return m_args; } + V3DefineRef(const string& name, const string& params, int pl) + : m_name(name), m_params(params), m_parenLevel(pl) {} + ~V3DefineRef() {} +}; + //************************************************************************* // Data for a preprocessor instantiation. @@ -88,14 +110,12 @@ struct V3PreProcImp : public V3PreProc { bool m_rawAtBol; ///< Last rawToken left us at beginning of line // For defines - string m_defName; // Define last name being defined - string m_defParams; // Define parameter list for next expansion + stack m_defRefs; // Pending definine substitution stack m_ifdefStack; // Stack of true/false emitting evaluations - vector m_defArgs; // List of define arguments unsigned m_defDepth; // How many `defines deep // Defines list - DefinesMap m_defines; // Map of defines + DefinesMap m_defines; // Map of defines // For getline() string m_lineChars; // Characters left for next line @@ -113,7 +133,7 @@ struct V3PreProcImp : public V3PreProc { private: // Internal methods void eof(); - string defineSubst(); + string defineSubst(V3DefineRef* refp); void addLineComment(int enter_exit_level); bool defExists(const string& name); @@ -122,6 +142,7 @@ private: FileLine* defFileline(const string& name); bool commentTokenMatch(string& cmdr, const char* strg); + string trimWhitespace(const string& strg); void parsingOn() { m_off--; @@ -149,7 +170,6 @@ public: V3PreProcImp(FileLine* fl) : V3PreProc(fl) { m_lexp = NULL; // Closed. m_state = ps_TOP; - m_defName = ""; m_off = 0; m_lineChars = ""; m_lastSym = ""; @@ -348,7 +368,15 @@ const char* V3PreProcImp::tokenName(int tok) { } } -string V3PreProcImp::defineSubst() { +string V3PreProcImp::trimWhitespace(const string& strg) { + string out = strg; + while (out.length()>0 && isspace(out[0])) { + out.erase(0,1); + } + return out; +} + +string V3PreProcImp::defineSubst(V3DefineRef* refp) { // Substitute out defines in a argumented define reference. // We could push the define text back into the lexer, but that's slow // and would make recursive definitions and parameter handling nasty. @@ -356,25 +384,27 @@ string V3PreProcImp::defineSubst() { // Note we parse the definition parameters and value here. If a // parameterized define is used many, many times, we could cache the // parsed result. - UINFO(4,"defineSubstIn `"<name()<<" "<params()<args().size(); i++) { + UINFO(4,"defineArg["<args()[i]<name()); + UINFO(4,"defineValue '"< argValueByName; { // Parse argument list into map unsigned numArgs=0; string argName; - for (const char* cp=m_defParams.c_str(); *cp; cp++) { + for (const char* cp=refp->params().c_str(); *cp; cp++) { if (*cp=='(') { } else if (argName=="" && isspace(*cp)) { } else if (isspace(*cp) || *cp==')' || *cp==',') { if (argName!="") { - if (m_defArgs.size() >= numArgs) { - argValueByName[argName] = m_defArgs[numArgs]; + if (refp->args().size() > numArgs) { + // A call `def( a ) must be equivelent to `def(a ), so trimWhitespace + // Note other sims don't trim trailing whitespace, so we don't either. + argValueByName[argName] = trimWhitespace(refp->args()[numArgs]); } numArgs++; //cout << " arg "<v3error("Define passed wrong number of arguments: "+m_defName+"\n"); - return " `"+m_defName+" "; + if (refp->args().size() != numArgs) { + fileline()->v3error("Define passed wrong number of arguments: "+refp->name()+"\n"); + return " `"+refp->name()+" "; } } - string out = " "; + string out = ""; { // Parse substitution define using arguments string argName; string prev; @@ -447,8 +477,7 @@ string V3PreProcImp::defineSubst() { } } - out += " "; - UINFO(4,"defineSubstOut "<lineno(), m_off, m_state, tokenName(tok), buf.c_str()); + fprintf (stderr, "%d: RAW %s s%d dr%d: %-10s: %s\n", + fileline()->lineno(), m_off?"of":"on", m_state, m_defRefs.size(), + tokenName(tok), buf.c_str()); } // On EOF, try to pop to upper level includes, as needed. @@ -651,7 +681,7 @@ int V3PreProcImp::getToken() { else if (m_stateFor==VP_DEFINE) { // m_lastSym already set. m_state = ps_DEFVALUE; - m_lexp->setStateDefValue(); + m_lexp->pushStateDefValue(); } else fileline()->v3fatalSrc("Bad case\n"); goto next_tok; @@ -698,7 +728,7 @@ int V3PreProcImp::getToken() { && isspace(m_lexp->m_defValue[m_lexp->m_defValue.length()-1-trailspace])) trailspace++; if (trailspace) m_lexp->m_defValue.erase(m_lexp->m_defValue.length()-trailspace,trailspace); // Define it - UINFO(4,"Define "<m_defValue<m_defValue, params); } } else { @@ -712,40 +742,57 @@ int V3PreProcImp::getToken() { } case ps_DEFPAREN: { if (tok==VP_TEXT && yyleng==1 && yytext[0]=='(') { - m_defArgs.clear(); m_state = ps_DEFARG; - m_lexp->setStateDefArg(); goto next_tok; } else { m_state = ps_TOP; - fileline()->v3error("Expecting ( to begin argument list for define reference `"<v3error("Expecting ( to begin argument list for define reference `"<name()); goto next_tok; } } case ps_DEFARG: { - if (tok==VP_DEFARG) { - UINFO(4," Defarg "<nextarg(refp->nextarg()+m_lexp->m_defValue); m_lexp->m_defValue=""; + if (tok==VP_DEFARG && yyleng==1 && yytext[0]==',') { + refp->args().push_back(refp->nextarg()); m_state = ps_DEFARG; - m_lexp->setStateDefArg(); + m_lexp->pushStateDefArg(); + refp->nextarg(""); goto next_tok; - } else if (tok==VP_TEXT && yyleng==1 && yytext[0]==')') { - m_defArgs.push_back(m_lexp->m_defValue); - string out = defineSubst(); - m_lexp->m_parenLevel = 0; + } else if (tok==VP_DEFARG && yyleng==1 && yytext[0]==')') { + refp->args().push_back(refp->nextarg()); + string out = defineSubst(refp); + // Substitute in and prepare for next action + // Similar code in non-parenthesized define (Search for END_OF_DEFARG) + m_defRefs.pop(); m_lexp->unputString(out.c_str()); - // Prepare for next action - m_defArgs.clear(); - m_state = ps_TOP; + if (m_defRefs.empty()) { + m_state = ps_TOP; + m_lexp->m_parenLevel = 0; + } + else { // Finished a defref inside a upper defref + refp = &(m_defRefs.top()); // We popped, so new top + m_lexp->m_parenLevel = refp->parenLevel(); + m_state = ps_DEFARG; + } + goto next_tok; + } else if (tok==VP_DEFREF) { + // Expand it, then state will come back here + // Value of building argument is data before the lower defref + // we'll append it when we push the argument. + break; + } else if (tok==VP_SYMBOL || tok==VP_STRING || VP_TEXT || VP_WHITE || VP_PSL) { + string rtn; rtn.assign(yytext,yyleng); + refp->nextarg(refp->nextarg()+rtn); goto next_tok; } else { fileline()->v3error("Expecting ) or , to end argument list for define reference. Found: "< m_state = ps_INCNAME; // Still - m_lexp->setStateIncFilename(); + m_lexp->pushStateIncFilename(); goto next_tok; } else if (tok==VP_DEFREF) { @@ -844,18 +891,17 @@ int V3PreProcImp::getToken() { else { string params = defParams(name); if (params=="0" || params=="") { // Found, as simple substitution - // Pack spaces around the define value, as there must be token boundaries around it. - // It also makes it more obvious where defines got substituted. - string out = " "+defValue(name)+" "; - UINFO(4,"Defref `"< "< '"<unputString(out.c_str()); goto next_tok; } else { // Found, with parameters UINFO(4,"Defref `"< parameterized"<m_parenLevel)); m_state = ps_DEFPAREN; m_stateFor = tok; + m_lexp->pushStateDefArg(); goto next_tok; } } diff --git a/test_regress/t/t_pp_display.pl b/test_regress/t/t_pp_display.pl new file mode 100755 index 000000000..18d89e53f --- /dev/null +++ b/test_regress/t/t_pp_display.pl @@ -0,0 +1,32 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id: t_delay.pl 965 2007-10-31 20:29:07Z wsnyder $ +# 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 +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + expect=>quotemeta( +'pre thrupre thrumid thrupost post: "right side" +left side: "right side" +left side : "right side " +left_side : "left_side " +na : "left_side " +prep ( midp1 left_side midp2 ( outp ) ) : "left_side " +na: "nana" +left_side left_side : "left_side left_side " +: "" +left side: "right side" +left side : "right side " +twoline: "first second" +*-* All Finished *-* +')); + +ok(1); +1; diff --git a/test_regress/t/t_pp_display.v b/test_regress/t/t_pp_display.v new file mode 100644 index 000000000..8ff1627ce --- /dev/null +++ b/test_regress/t/t_pp_display.v @@ -0,0 +1,56 @@ +// $Id: t_delay.v 965 2007-10-31 20:29:07Z wsnyder $ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2008 by Wilson Snyder. + +module t; + wire d1 = 1'b1; + wire d2 = 1'b1; + wire d3 = 1'b1; + wire o1,o2,o3; + add1 add1 (d1,o1); + add2 add2 (d2,o2); + +`define ls left_side +`define rs left_side +`define noarg na +`define thru(x) x +`define thruthru `ls `rs // Doesn't expand +`define msg(x,y) `"x: `\`"y`\`"`" + initial begin + //$display(`msg( \`, \`)); // Illegal + $display(`msg(pre `thru(thrupre `thru(thrumid) thrupost) post,right side)); + $display(`msg(left side,right side)); + $display(`msg( left side , right side )); + $display(`msg( `ls , `rs )); + $display(`msg( `noarg , `rs )); + $display(`msg( prep ( midp1 `ls midp2 ( outp ) ) , `rs )); + $display(`msg(`noarg,`noarg`noarg)); + $display(`msg( `thruthru , `thruthru )); // Results vary between simulators + $display(`msg(`thru(),)); // Empty + $display(`msg(`thru(left side),`thru(right side))); + $display(`msg( `thru( left side ) , `thru( right side ) )); + +`define twoline first \ + second + $display(`msg(twoline, `twoline)); + + //$display(`msg(left side, \ right side \ )); // Not sure \{space} is legal. + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +`define ADD_UP(a,c) \ +wire tmp_``a = a; \ +wire tmp_``c = tmp_``a + 1; \ +assign c = tmp_``c ; + +module add1 ( input wire d1, output wire o1); + `ADD_UP(d1,o1) // expansion is OK +endmodule +module add2 ( input wire d2, output wire o2); + `ADD_UP( d2 , o2 ) // expansion is bad +endmodule +// `ADD_UP( \d3 , \o3 ) // This really is illegal diff --git a/test_regress/t/t_preproc.out b/test_regress/t/t_preproc.out index 8a8b4ba1e..22ccea938 100644 --- a/test_regress/t/t_preproc.out +++ b/test_regress/t/t_preproc.out @@ -15,7 +15,7 @@ At file t/t_preproc_inc2.v line 4 - + `line 6 "t/t_preproc_inc2.v" 0 `line 1 "t/t_preproc_inc3.v" 1 `line 2 "inc3_a_filename_from_line_directive" 0 @@ -41,7 +41,7 @@ At file t/t_preproc_inc2.v line 4 `line 18 "inc3_a_filename_from_line_directive" 2 `line 6 "t/t_preproc_inc2.v" 0 - + `line 7 "t/t_preproc_inc2.v" 2 `line 9 "t/t_preproc.v" 0 @@ -78,8 +78,8 @@ text. - foo bar - foobar2 +foo bar +foobar2 @@ -91,7 +91,7 @@ text. - first part second part third part +first part second part third part Line_Preproc_Check 49 @@ -100,37 +100,46 @@ Line_Preproc_Check 49 - deep deep +deep deep "Inside: `nosubst" - "`nosubst" +"`nosubst" - x y LLZZ x y - p q LLZZ p q r s LLZZ r s LLZZ p q LLZZ p q r s LLZZ r s +x y LLZZ x y +p q LLZZ p q r s LLZZ r s LLZZ p q LLZZ p q r s LLZZ r s - firstline comma","line LLZZ firstline comma","line +firstline comma","line LLZZ firstline comma","line - x y LLZZ "a" y +x y LLZZ "a" y - (a,b) (a,b) +(a,b)(a,b) -$display( "left side: \" right side\"" ) +$display("left side: \"right side\"") - bar_suffix +bar_suffix - $c("Zap(\"",bug1,"\");"); ; - $c("Zap(\"","bug2","\");"); ; +$c("Zap(\"",bug1,"\");");; +$c("Zap(\"","bug2","\");");; + + + + + + + +wire tmp_d1 = d1; wire tmp_o1 = tmp_d1 + 1; assign o1 = tmp_o1 ; +wire tmp_d2 = d2 ; wire tmp_o2 = tmp_d2 + 1; assign o2 = tmp_o2 ; @@ -139,7 +148,7 @@ $display( "left side: \" right side\"" ) -`line 95 "t/t_preproc.v" 0 +`line 104 "t/t_preproc.v" 0 -Line_Preproc_Check 96 -`line 97 "t/t_preproc.v" 2 +Line_Preproc_Check 105 +`line 106 "t/t_preproc.v" 2 diff --git a/test_regress/t/t_preproc.v b/test_regress/t/t_preproc.v index 04ceeca3c..19af7a196 100644 --- a/test_regress/t/t_preproc.v +++ b/test_regress/t/t_preproc.v @@ -86,6 +86,15 @@ $display(`msg(left side, right side)) `zap(bug1); `zap("bug2"); +// rt.cpan.org bug34429 +`define ADD_UP(a,c) \ +wire tmp_``a = a; \ +wire tmp_``c = tmp_``a + 1; \ +assign c = tmp_``c ; + +`ADD_UP(d1,o1) // expansion is OK +`ADD_UP( d2 , o2 ) // expansion is bad + //=========================================================================== // Ifdef diff --git a/test_regress/t/t_preproc_psl_on.out b/test_regress/t/t_preproc_psl_on.out index 3f935baf2..0f2ad24a7 100644 --- a/test_regress/t/t_preproc_psl_on.out +++ b/test_regress/t/t_preproc_psl_on.out @@ -63,7 +63,7 @@ Hello in t_preproc_psl.v - psl assert always cyc !=10; + psl assert always cyc!=10; `psl