Support 1800-2009 defines with default arguments.

This commit is contained in:
Wilson Snyder 2009-12-21 08:54:39 -05:00
parent 9b0d26aedd
commit e7cbefa316
8 changed files with 293 additions and 87 deletions

View File

@ -22,7 +22,7 @@ indicates the contributor was also the author of the fix; Thanks!
*** Support $sformat and $swrite.
*** Support `undefineall.
*** Support 1800-2009 define defaults and `undefineall.
*** Add VARHIDDEN warning when signal name hides module name.

View File

@ -116,6 +116,7 @@ class V3PreLex {
bool m_pedantic; // Obey standard; don't Substitute `__FILE__ and `__LINE__
// State from lexer
int m_formalLevel; // Parenthesis counting inside def formals
int m_parenLevel; // Parenthesis counting inside def args
int m_pslParenLevel;// PSL Parenthesis (){} counting, so we can find final ;
bool m_pslMoreNeeded;// Next // comment is really psl
@ -126,6 +127,7 @@ class V3PreLex {
m_fp = fp;
m_keepComments = 0;
m_pedantic = false;
m_formalLevel = 0;
m_parenLevel = 0;
m_pslParenLevel = 0;
m_pslMoreNeeded = false;

View File

@ -30,18 +30,20 @@
V3PreLex* V3PreLex::s_currentLexp = NULL; // Current lexing point
#define LEXP V3PreLex::s_currentLexp
// Prevent conflicts from perl version
static void linenoInc() {V3PreLex::s_currentLexp->incLineno();}
static void linenoInc() {LEXP->incLineno();}
static bool optPsl() { return V3PreProc::optPsl(); }
static bool pedantic() { return V3PreLex::s_currentLexp->m_pedantic; }
static void yyerror(char* msg) { V3PreLex::s_currentLexp->m_curFilelinep->v3error(msg); }
static void yyerrorf(const char* msg) { V3PreLex::s_currentLexp->m_curFilelinep->v3error(msg); }
static void appendDefValue(const char* t,int l) { V3PreLex::s_currentLexp->appendDefValue(t,l); }
static int pslParenLevel() { return V3PreLex::s_currentLexp->m_pslParenLevel; }
static void pslParenLevelInc() { V3PreLex::s_currentLexp->m_pslParenLevel++; }
static void pslParenLevelDec() { if (pslParenLevel()) V3PreLex::s_currentLexp->m_pslParenLevel--; }
static bool pslMoreNeeded() { return V3PreLex::s_currentLexp->m_pslMoreNeeded; }
static void pslMoreNeeded(bool flag) { V3PreLex::s_currentLexp->m_pslMoreNeeded = flag; }
static bool pedantic() { return LEXP->m_pedantic; }
static void yyerror(char* msg) { LEXP->m_curFilelinep->v3error(msg); }
static void yyerrorf(const char* msg) { LEXP->m_curFilelinep->v3error(msg); }
static void appendDefValue(const char* t,int l) { LEXP->appendDefValue(t,l); }
static int pslParenLevel() { return LEXP->m_pslParenLevel; }
static void pslParenLevelInc() { LEXP->m_pslParenLevel++; }
static void pslParenLevelDec() { if (pslParenLevel()) LEXP->m_pslParenLevel--; }
static bool pslMoreNeeded() { return LEXP->m_pslMoreNeeded; }
static void pslMoreNeeded(bool flag) { LEXP->m_pslMoreNeeded = flag; }
/**********************************************************************/
%}
@ -75,7 +77,7 @@ psl [p]sl
/**************************************************************/
%%
<INITIAL>^{ws}*"`line"{ws}+.*{crnl} { V3PreLex::s_currentLexp->lineDirective(yytext);
<INITIAL>^{ws}*"`line"{ws}+.*{crnl} { LEXP->lineDirective(yytext);
return(VP_LINE); }
/* Special directives we recognize */
@ -92,13 +94,13 @@ psl [p]sl
/* Optional directives we recognize */
<INITIAL>"`__FILE__" { if (!pedantic()) {
static string rtnfile;
rtnfile = '"'; rtnfile += V3PreLex::s_currentLexp->m_curFilelinep->cfilename();
rtnfile = '"'; rtnfile += LEXP->m_curFilelinep->cfilename();
rtnfile += '"'; yytext=(char*)rtnfile.c_str(); yyleng = rtnfile.length();
return (VP_STRING);
} else return(VP_DEFREF); }
<INITIAL>"`__LINE__" { if (!pedantic()) {
static char buf[10];
sprintf(buf, "%d",V3PreLex::s_currentLexp->m_curFilelinep->lineno());
sprintf(buf, "%d",LEXP->m_curFilelinep->lineno());
yytext = buf; yyleng = strlen(yytext); return (VP_TEXT);
} else return(VP_DEFREF); }
<INITIAL>"`error" { if (!pedantic()) return (VP_ERROR); else return(VP_DEFREF); }
@ -108,10 +110,10 @@ psl [p]sl
<STRMODE><<EOF>> { linenoInc(); yyerrorf("EOF in unterminated string"); yyleng=0; yyterminate(); }
<STRMODE>{crnl} { linenoInc(); yyerrorf("Unterminated string"); BEGIN(INITIAL); }
<STRMODE>[^\"\\] { yymore(); }
<STRMODE>{backslash}{crnl} { linenoInc(); yymore(); }
<STRMODE>{backslash}{crnl} { linenoInc(); yymore(); }
<STRMODE>{backslash}. { yymore(); }
<STRMODE>{quote} { yy_pop_state();
if (V3PreLex::s_currentLexp->m_parenLevel) appendDefValue(yytext,yyleng);
if (LEXP->m_parenLevel || LEXP->m_formalLevel) { appendDefValue(yytext,yyleng); yyleng=0; }
else return (VP_STRING); }
/* Protected blocks */
@ -130,21 +132,24 @@ psl [p]sl
/* Reading definition formal parenthesis (or not) to begin formal arguments */
/* Note '(' must IMMEDIATELY follow definition name */
<DEFFPAR>[(] { appendDefValue("(",1); BEGIN(DEFFORM); }
<DEFFPAR>[(] { appendDefValue("(",1); LEXP->m_formalLevel=1; BEGIN(DEFFORM); }
<DEFFPAR>{crnl} { yy_pop_state(); unput('\n'); yyleng=0; return VP_DEFFORM; } /* DEFVAL will later grab the return */
<DEFFPAR><<EOF>> { yy_pop_state(); return VP_DEFFORM; } /* empty formals */
<DEFFPAR>. { yy_pop_state(); unput(yytext[yyleng-1]); yyleng=0; return VP_DEFFORM; } /* empty formals */
/* Reading definition formals */
<DEFFORM>[(] { yy_pop_state(); yyerrorf("Extra ( in define formal arguments.\n"); yyleng=0; return VP_DEFFORM; }
<DEFFORM>[)] { yy_pop_state(); appendDefValue(yytext,yyleng); yyleng=0; return VP_DEFFORM; }
<DEFFORM>[(] { appendDefValue(yytext,yyleng); yyleng=0; ++LEXP->m_formalLevel; }
<DEFFORM>[)] { appendDefValue(yytext,yyleng); yyleng=0; if ((--LEXP->m_formalLevel)==0) { yy_pop_state(); return VP_DEFFORM; } }
<DEFFORM>"/*" { yy_push_state(CMTMODE); yymore(); }
<DEFFORM>"//"[^\n\r]* { return (VP_COMMENT);}
<DEFFORM>{drop} { }
<DEFFORM>{drop} { }
<DEFFORM><<EOF>> { linenoInc(); yy_pop_state(); yyerrorf("Unterminated ( in define formal arguments."); yyleng=0; return VP_DEFFORM; }
<DEFFORM>{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */
<DEFFORM>[\\]{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */
<DEFFORM>[^\/\*\n\r\\()]+ |
<DEFFORM>{quote} { yy_push_state(STRMODE); yymore(); }
<DEFFORM>[{\[] { LEXP->m_formalLevel++; appendDefValue(yytext,yyleng); }
<DEFFORM>[}\]] { LEXP->m_formalLevel--; appendDefValue(yytext,yyleng); }
<DEFFORM>[^\/\*\n\r\\(){}\[\]\"]+ |
<DEFFORM>[\\][^\n\r] |
<DEFFORM>. { appendDefValue(yytext,yyleng); }
@ -166,26 +171,24 @@ psl [p]sl
<ARGMODE><<EOF>> { yyerrorf("EOF in define argument list\n"); yyleng = 0; yyterminate(); }
<ARGMODE>{crnl} { linenoInc(); yytext=(char*)"\n"; yyleng=1; return(VP_WHITE); }
<ARGMODE>{quote} { yy_push_state(STRMODE); yymore(); }
<ARGMODE>[{\[] { V3PreLex::s_currentLexp->m_parenLevel++;
appendDefValue(yytext,yyleng); }
<ARGMODE>[}\]] { V3PreLex::s_currentLexp->m_parenLevel--;
appendDefValue(yytext,yyleng); }
<ARGMODE>[(] { V3PreLex::s_currentLexp->m_parenLevel++;
<ARGMODE>[{\[] { LEXP->m_parenLevel++; appendDefValue(yytext,yyleng); }
<ARGMODE>[}\]] { LEXP->m_parenLevel--; appendDefValue(yytext,yyleng); }
<ARGMODE>[(] { LEXP->m_parenLevel++;
// Note paren level 0 means before "(" of starting args
// Level 1 means "," between arguments
// Level 2+ means one argument's internal ()
if (V3PreLex::s_currentLexp->m_parenLevel>1) {
if (LEXP->m_parenLevel>1) {
appendDefValue(yytext,yyleng);
} else {
return (VP_TEXT);
}}
<ARGMODE>[)] { V3PreLex::s_currentLexp->m_parenLevel--;
if (V3PreLex::s_currentLexp->m_parenLevel>0) {
<ARGMODE>[)] { LEXP->m_parenLevel--;
if (LEXP->m_parenLevel>0) {
appendDefValue(yytext,yyleng);
} else {
yy_pop_state(); return (VP_DEFARG);
}}
<ARGMODE>[,] { if (V3PreLex::s_currentLexp->m_parenLevel>1) {
<ARGMODE>[,] { if (LEXP->m_parenLevel>1) {
appendDefValue(yytext,yyleng);
} else {
yy_pop_state(); return (VP_DEFARG);

View File

@ -161,7 +161,7 @@ private:
FileLine* defFileline(const string& name);
bool commentTokenMatch(string& cmdr, const char* strg);
string trimWhitespace(const string& strg);
string trimWhitespace(const string& strg, bool trailing);
void unputString(const string& strg);
void parsingOn() {
@ -411,10 +411,19 @@ void V3PreProcImp::unputString(const string& strg) {
m_lexp->scanBytes(strg);
}
string V3PreProcImp::trimWhitespace(const string& strg) {
string V3PreProcImp::trimWhitespace(const string& strg, bool trailing) {
// Remove leading whitespace
string out = strg;
while (out.length()>0 && isspace(out[0])) {
out.erase(0,1);
string::size_type leadspace = 0;
while (out.length() > leadspace
&& isspace(out[leadspace])) leadspace++;
if (leadspace) out.erase(0,leadspace);
// Remove trailing whitespace
if (trailing) {
string::size_type trailspace = 0;
while (out.length() > trailspace
&& isspace(out[out.length()-1-trailspace])) trailspace++;
if (trailspace) out.erase(out.length()-trailspace,trailspace);
}
return out;
}
@ -429,7 +438,7 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) {
// parsed result.
UINFO(4,"defineSubstIn `"<<refp->name()<<" "<<refp->params()<<endl);
for (unsigned i=0; i<refp->args().size(); i++) {
UINFO(4,"defineArg["<<i<<"] = "<<refp->args()[i]<<endl);
UINFO(4,"defineArg["<<i<<"] = '"<<refp->args()[i]<<"'"<<endl);
}
// Grab value
string value = defValue(refp->name());
@ -439,27 +448,63 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) {
{ // Parse argument list into map
unsigned numArgs=0;
string argName;
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 (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]);
int paren = 1; // (), {} and [] can use same counter, as must be matched pair per spec
string token;
bool quote = false;
bool haveDefault = false;
// Note there's a leading ( and trailing ), so parens==1 is the base parsing level
const char* cp=refp->params().c_str();
if (*cp == '(') cp++;
for (; *cp; cp++) {
//UINFO(4," Parse Paren="<<paren<<" Arg="<<numArgs<<" token='"<<token<<"' Parse="<<cp<<endl);
if (!quote && paren==1) {
if (*cp==')' || *cp==',') {
string value;
if (haveDefault) { value=token; } else { argName=token; }
argName = trimWhitespace(argName,true);
UINFO(4," Got Arg="<<numArgs<<" argName='"<<argName<<"' default='"<<value<<"'"<<endl);
// Parse it
if (argName!="") {
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.
string arg = trimWhitespace(refp->args()[numArgs], false);
if (arg != "") value = arg;
} else if (!haveDefault) {
fileline()->v3error("Define missing argument '"+argName+"' for: "+refp->name()+"\n");
return " `"+refp->name()+" ";
}
numArgs++;
}
numArgs++;
//cout << " arg "<<argName<<endl;
argValueByName[argName] = value;
// Prepare for next
argName = "";
token = "";
haveDefault = false;
continue;
}
else if (*cp=='=') {
haveDefault = true;
argName = token;
token = "";
continue;
}
argName = "";
} else if ( isalpha(*cp) || *cp=='_'
|| (argName!="" && (isdigit(*cp) || *cp=='$'))) {
argName += *cp;
}
if (cp[0]=='\\' && cp[1]) {
token += cp[0]; // \{any} Put out literal next character
token += cp[1];
cp++;
continue;
}
if (!quote) {
if (*cp=='(' || *cp=='{' || *cp=='[') paren++;
else if (*cp==')' || *cp=='}' || *cp==']') paren--;
}
if (*cp=='"') quote=!quote;
if (*cp) token += *cp;
}
if (refp->args().size() != numArgs) {
fileline()->v3error("Define passed wrong number of arguments: "+refp->name()+"\n");
if (refp->args().size() > numArgs) {
fileline()->v3error("Define passed too many arguments: "+refp->name()+"\n");
return " `"+refp->name()+" ";
}
}
@ -768,42 +813,27 @@ int V3PreProcImp::getToken() {
if (tok == VP_DEFVALUE) {
if (debug()) cout<<"DefValue='"<<m_lexp->m_defValue<<"' formals='"<<m_formals<<"'\n";
// Add any formals
string formAndValue = m_formals + m_lexp->m_defValue;
string formals = m_formals;
string value = m_lexp->m_defValue;
// Remove returns
for (unsigned i=0; i<formAndValue.length(); i++) {
if (formAndValue[i] == '\n') {
formAndValue[i] = ' ';
for (unsigned i=0; i<formals.length(); i++) {
if (formals[i] == '\n') {
formals[i] = ' ';
newlines += "\n";
}
}
for (unsigned i=0; i<value.length(); i++) {
if (value[i] == '\n') {
value[i] = ' ';
newlines += "\n";
}
}
if (!m_off) {
string params;
if (formAndValue=="" || isspace(formAndValue[0])) {
// Define without parameters
} else if (formAndValue[0] == '(') {
string::size_type paren = formAndValue.find(")");
if (paren == string::npos) {
fileline()->v3error("Missing ) to end define arguments.");
} else {
params = formAndValue.substr(0, paren+1);
formAndValue.replace(0, paren+1, "");
}
} else {
fileline()->v3error("Missing space or paren to start define value.");
}
// Remove leading whitespace
unsigned leadspace = 0;
while (formAndValue.length() > leadspace
&& isspace(formAndValue[leadspace])) leadspace++;
if (leadspace) formAndValue.erase(0,leadspace);
// Remove trailing whitespace
unsigned trailspace = 0;
while (formAndValue.length() > trailspace
&& isspace(formAndValue[formAndValue.length()-1-trailspace])) trailspace++;
if (trailspace) formAndValue.erase(formAndValue.length()-trailspace,trailspace);
// Remove leading and trailing whitespace
value = trimWhitespace(value, true);
// Define it
UINFO(4,"Define "<<m_lastSym<<" = '"<<formAndValue<<"'"<<endl);
define(fileline(), m_lastSym, formAndValue, params, false);
UINFO(4,"Define "<<m_lastSym<<" "<<formals<<" = '"<<value<<"'"<<endl);
define(fileline(), m_lastSym, value, formals, false);
}
} else {
fileline()->v3fatalSrc("Bad define text\n");

View File

@ -131,7 +131,7 @@ private:
outrefp = outp->castSel()->fromp()->castVarRef();
width = outp->castSel()->widthConst();
} else {
v3error("Can't find LHS varref");
nodep->v3error("Can't find LHS varref");
}
outrefp->lvalue(true);
AstVar* varp = outrefp->varp();
@ -185,7 +185,7 @@ private:
} else if (outp->castSel()) {
outrefp = outp->castSel()->fromp()->castVarRef();
} else {
v3error("Can't find LHS varref");
nodep->v3error("Can't find LHS varref");
}
createEnableVar(outp, outrefp, enrhsp, outrhsp->width());
@ -215,7 +215,7 @@ private:
enrhsp = new AstReplicate(enrhsp->fileline(), enrhsp, new AstConst(enrhsp->fileline(), V3Number(enrhsp->fileline(), 32, enp->width())));
enrhsp->width(enp->width(), enp->widthMin());
} else {
v3error("Don't know how to deal with selection logic wider than 1 bit");
enrhsp->v3error("Don't know how to deal with selection logic wider than 1 bit");
}
}
m_modp->addStmtp(enp);
@ -351,7 +351,7 @@ private:
// not sure what I should do here other than error that they are mixing low-Z and tristate drivers.
// The other scenerio, and probably more likely, is that they are using a high-Z construct that
// is not supported. Improving the high-Z detection logic will reduce the occurance of this failure.
v3error("Mixing tristate and low-Z drivers. Perhaps you are using a high-Z construct not supported");
nodep->v3error("Mixing tristate and low-Z drivers. Perhaps you are using a high-Z construct not supported");
} else {
UINFO(9, " No tristates found on " << lhsp <<endl);
}

View File

@ -0,0 +1,75 @@
`line 1 "t/t_preproc_def09.v" 1
'initial $display("start", "msg1" , "msg2" , "end");'
'initial $display("start", "msg1" , "msg2" , "end");'
'initial $display("start", " msg1" , , "end");'
'initial $display("start", " msg1" , , "end");'
'initial $display("start", , "msg2 ", "end");'
'initial $display("start", , "msg2 ", "end");'
'initial $display("start", , , "end");'
'initial $display("start", , , "end");'
'initial $display("start", , , "end");'
'initial $display("start", , , "end");'
'$display(5,,2,,3 );'
'$display(5,,2,,3);'
'$display(1 ,,"B",,3 );'
'$display(1 ,,"B",,3 );'
'$display(5,,2,,);'
'$display(5,,2,,);'
'$display(1,,,,3);'
'$display(5,,,,"C");'
'$display(5,,2,,"C");'
'$display(5,,2,,"C");'
'$display(5,,2,,"C");'
'$display(5,,2,,"C");'
'$display(1 ,,0,,"C");'
'$display(1 ,,0,,"C");'
'$display(5,,0,,"C");'
'$display(5,,0,,"C");'
'b + 1 + 42 + a '
'b + 1 + 42 + a'
'"==)" "((((" () ';
'"==)" "((((" () ';
'(6) (eq=al) ZOT'
HERE-71 - Line71
`line 74 "t/t_preproc_def09.v" 2

View File

@ -0,0 +1,23 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003-2009 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.
my $stdout_filename = "$Self->{obj_dir}/$Self->{name}__test.vpp";
if (!$Self->{v3}) {
ok(1);
} else {
compile (
v_flags2 => ['-E'],
verilator_make_gcc=>0,
stdout_filename => $stdout_filename,
);
ok(files_identical($stdout_filename, "t/$Self->{name}.out"));
}
1;

View File

@ -0,0 +1,73 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2009 by Wilson Snyder.
`undefineall
// Definitions as speced
// Note there are trailing spaces, which spec doesn't show properly
`define D(x,y) initial $display("start", x , y, "end");
'`D( "msg1" , "msg2" )'
'initial $display("start", "msg1" , "msg2" , "end");'
'`D( " msg1", )'
'initial $display("start", " msg1" , , "end");'
'`D(, "msg2 ")'
'initial $display("start", , "msg2 ", "end");'
'`D(,)'
'initial $display("start", , , "end");'
'`D( , )'
'initial $display("start", , , "end");'
//`D("msg1") // ILLEGAL: only one argument
//`D() // ILLEGAL: only one empty argument
//`D(,,) // ILLEGAL: more actual than formal arguments
// Defaults:
`define MACRO1(a=5,b="B",c) $display(a,,b,,c);
'`MACRO1 ( , 2, 3 )'
'$display(5,,2,,3);'
'`MACRO1 ( 1 , , 3 )'
'$display(1 ,,"B",,3 );'
'`MACRO1 ( , 2, )'
'$display(5,,2,,);'
//`MACRO1 ( 1 ) // ILLEGAL: b and c omitted, no default for c
`define MACRO2(a=5, b, c="C") $display(a,,b,,c);
'`MACRO2 (1, , 3)'
'$display(5,,,,"C");'
'`MACRO2 (, 2, )'
'$display(5,,2,,"C");'
'`MACRO2 (, 2)'
'$display(5,,2,,"C");'
`define MACRO3(a=5, b=0, c="C") $display(a,,b,,c);
'`MACRO3 ( 1 )'
'$display(1 ,,0,,"C");'
'`MACRO3 ( )'
'$display(5,,0,,"C");'
//`MACRO3 // ILLEGAL: parentheses required
`define DTOP(a,b) a + b
'`DTOP( `DTOP(b,1), `DTOP(42,a) )'
'b + 1 + 42 + a'
// Local tests
`define MACROQUOTE(a="==)",b="((((",c=() ) 'a b c'
`MACROQUOTE();
'"==)" "((((" () ';
// Also check our line counting doesn't go bad
`define MACROPAREN(a=(6),
b=(eq=al),
c) 'a b c'
`MACROPAREN(
,,
ZOT)
HERE-`__LINE__ - Line71
//======================================================================