Parse SystemVerilog syntax for task calls.

Tasks call arguments may be dropped in favor of default values.
Allow for that in the syntax. This requires a little handling
of the non-SystemVerilog case during elaboration.
This commit is contained in:
Stephen Williams 2012-03-10 09:50:41 -08:00
parent da743c3b2c
commit dbc6f0cff2
3 changed files with 121 additions and 106 deletions

View File

@ -196,18 +196,6 @@ class PCallTask : public Statement {
const pform_name_t& path() const; const pform_name_t& path() const;
unsigned nparms() const { return parms_.size(); }
PExpr*&parm(unsigned idx)
{ assert(idx < parms_.size());
return parms_[idx];
}
PExpr* parm(unsigned idx) const
{ assert(idx < parms_.size());
return parms_[idx];
}
virtual void dump(ostream&out, unsigned ind) const; virtual void dump(ostream&out, unsigned ind) const;
virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual NetProc* elaborate(Design*des, NetScope*scope) const;

View File

@ -2825,12 +2825,12 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const
des->errors += 1; des->errors += 1;
} }
unsigned parm_count = nparms(); unsigned parm_count = parms_.size();
/* Catch the special case that the system task has no /* Catch the special case that the system task has no
parameters. The "()" string will be parsed as a single parameters. The "()" string will be parsed as a single
empty parameter, when we really mean no parameters at all. */ empty parameter, when we really mean no parameters at all. */
if ((nparms() == 1) && (parm(0) == 0)) if ((parm_count== 1) && (parms_[0] == 0))
parm_count = 0; parm_count = 0;
svector<NetExpr*>eparms (parm_count); svector<NetExpr*>eparms (parm_count);
@ -2838,7 +2838,7 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const
perm_string name = peek_tail_name(path_); perm_string name = peek_tail_name(path_);
for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { for (unsigned idx = 0 ; idx < parm_count ; idx += 1) {
PExpr*ex = parm(idx); PExpr*ex = parms_[idx];
if (ex != 0) { if (ex != 0) {
eparms[idx] = elab_sys_task_arg(des, scope, name, idx, ex); eparms[idx] = elab_sys_task_arg(des, scope, name, idx, ex);
} else { } else {
@ -2930,9 +2930,20 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const
} }
assert(def); assert(def);
if (nparms() != def->port_count()) {
unsigned parm_count = parms_.size();
// Handle special case that the definition has no arguments
// but the parser found a simgle nul argument. This is an
// argument of the parser allowing for the possibility of
// default values for argumets: The parser cannot tell the
// difference between "func()" and "func(<default>)".
if (def->port_count() == 0 && parm_count == 1 && parms_[0] == 0)
parm_count = 0;
if (parm_count != def->port_count()) {
cerr << get_fileline() << ": error: Port count mismatch in call to ``" cerr << get_fileline() << ": error: Port count mismatch in call to ``"
<< path_ << "''. Got " << nparms() << path_ << "''. Got " << parm_count
<< " ports, expecting " << def->port_count() << " ports." << endl; << " ports, expecting " << def->port_count() << " ports." << endl;
des->errors += 1; des->errors += 1;
return 0; return 0;
@ -2942,7 +2953,7 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const
/* Handle non-automatic tasks with no parameters specially. There is /* Handle non-automatic tasks with no parameters specially. There is
no need to make a sequential block to hold the generated code. */ no need to make a sequential block to hold the generated code. */
if ((nparms() == 0) && !task->is_auto()) { if ((parm_count == 0) && !task->is_auto()) {
cur = new NetUTask(task); cur = new NetUTask(task);
cur->set_line(*this); cur->set_line(*this);
return cur; return cur;
@ -2974,7 +2985,23 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const
expression the r-value. We know by definition that the port expression the r-value. We know by definition that the port
is a reg type, so this elaboration is pretty obvious. */ is a reg type, so this elaboration is pretty obvious. */
for (unsigned idx = 0 ; idx < nparms() ; idx += 1) { for (unsigned idx = 0 ; idx < parm_count ; idx += 1) {
if (parms_[idx] == 0 && !gn_system_verilog()) {
cerr << get_fileline() << ": error: "
<< "Missing argument " << (idx+1)
<< " of call to task." << endl;
des->errors += 1;
continue;
}
if (parms_[idx] == 0) {
cerr << get_fileline() << ": sorry: "
<< "Implicit arguments (arg " << (idx+1)
<< ") not supported." << endl;
des->errors += 1;
continue;
}
NetNet*port = def->port(idx); NetNet*port = def->port(idx);
assert(port->port_type() != NetNet::NOT_A_PORT); assert(port->port_type() != NetNet::NOT_A_PORT);
@ -3015,7 +3042,7 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const
expression that can be a target to a procedural expression that can be a target to a procedural
assignment, including a memory word. */ assignment, including a memory word. */
for (unsigned idx = 0 ; idx < nparms() ; idx += 1) { for (unsigned idx = 0 ; idx < parm_count ; idx += 1) {
NetNet*port = def->port(idx); NetNet*port = def->port(idx);

136
parse.y
View File

@ -513,8 +513,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector<Statement*>
%type <enum_type> enum_data_type %type <enum_type> enum_data_type
%type <wires> task_item task_item_list task_item_list_opt %type <wires> task_item task_item_list task_item_list_opt
%type <wires> task_port_item tf_port_item tf_port_list tf_port_list_opt %type <wires> tf_port_declaration tf_port_item tf_port_list tf_port_list_opt
%type <wires> function_item function_item_list %type <wires> function_item function_item_list function_item_list_opt
%type <named_pexpr> port_name parameter_value_byname %type <named_pexpr> port_name parameter_value_byname
%type <named_pexprs> port_name_list parameter_value_byname_list %type <named_pexprs> port_name_list parameter_value_byname_list
@ -719,6 +719,7 @@ class_item /* IEEE1800-2005: A.1.8 */
/* IEEE1800 A.1.8: class_constructor_declaration */ /* IEEE1800 A.1.8: class_constructor_declaration */
: method_qualifier_opt K_function K_new '(' tf_port_list_opt ')' ';' : method_qualifier_opt K_function K_new '(' tf_port_list_opt ')' ';'
function_item_list_opt
statement_or_null_list_opt statement_or_null_list_opt
K_endfunction endnew_opt K_endfunction endnew_opt
{ yyerror(@3, "sorry: Class constructors not supported yet."); { yyerror(@3, "sorry: Class constructors not supported yet.");
@ -730,6 +731,7 @@ class_item /* IEEE1800-2005: A.1.8 */
be K_super ("this.new" makes little sense) but that would be K_super ("this.new" makes little sense) but that would
cause a conflict. */ cause a conflict. */
| method_qualifier_opt K_function K_new '(' tf_port_list_opt ')' ';' | method_qualifier_opt K_function K_new '(' tf_port_list_opt ')' ';'
function_item_list_opt
implicit_class_handle '.' K_new '(' expression_list_with_nuls ')' implicit_class_handle '.' K_new '(' expression_list_with_nuls ')'
statement_or_null_list_opt statement_or_null_list_opt
K_endfunction endnew_opt K_endfunction endnew_opt
@ -1454,6 +1456,45 @@ task_declaration /* IEEE1800-2005: A.2.7 */
; ;
tf_port_declaration /* IEEE1800-2005: A.2.7 */
: port_direction K_reg_opt unsigned_signed_opt range_opt list_of_identifiers ';'
{ svector<PWire*>*tmp = pform_make_task_ports(@1, $1,
$2 ? IVL_VT_LOGIC :
IVL_VT_NO_TYPE,
$3, $4, $5);
$$ = tmp;
}
/* When the port is an integer, infer a signed vector of the integer
shape. Generate a range ([31:0]) to make it work. */
| port_direction K_integer list_of_identifiers ';'
{ list<index_component_t>*range_stub = make_range_from_width(integer_width);
svector<PWire*>*tmp = pform_make_task_ports(@1, $1, IVL_VT_LOGIC, true,
range_stub, $3, true);
$$ = tmp;
}
/* Ports can be time with a width of [63:0] (unsigned). */
| port_direction K_time list_of_identifiers ';'
{ list<index_component_t>*range_stub = make_range_from_width(64);
svector<PWire*>*tmp = pform_make_task_ports(@1, $1, IVL_VT_LOGIC, false,
range_stub, $3);
$$ = tmp;
}
/* Ports can be real or realtime. */
| port_direction real_or_realtime list_of_identifiers ';'
{ svector<PWire*>*tmp = pform_make_task_ports(@1, $1, IVL_VT_REAL, false,
0, $3);
$$ = tmp;
}
;
/* These rules for tf_port_item are slightly expanded from the /* These rules for tf_port_item are slightly expanded from the
strict rules in the LRM to help with LALR parsing. strict rules in the LRM to help with LALR parsing.
@ -3071,12 +3112,20 @@ expr_primary
/* A function_item_list borrows the task_port_item run to match /* A function_item_list borrows the task_port_item run to match
declarations of ports. We check later to make sure there are no declarations of ports. We check later to make sure there are no
output or inout ports actually used. */ output or inout ports actually used.
The function_item is the same as tf_item_declaration. */
function_item_list_opt
: function_item_list { $$ = $1; }
| { $$ = 0; }
;
function_item_list function_item_list
: function_item : function_item
{ $$ = $1; } { $$ = $1; }
| function_item_list function_item | function_item_list function_item
{ if ($1 && $2) { { /* */
if ($1 && $2) {
svector<PWire*>*tmp = new svector<PWire*>(*$1, *$2); svector<PWire*>*tmp = new svector<PWire*>(*$1, *$2);
delete $1; delete $1;
delete $2; delete $2;
@ -3090,7 +3139,7 @@ function_item_list
; ;
function_item function_item
: task_port_item : tf_port_declaration
{ $$ = $1; } { $$ = $1; }
| block_item_decl | block_item_decl
{ $$ = 0; } { $$ = 0; }
@ -5700,7 +5749,7 @@ statement /* This is roughly statement_item in the LRM */
$$ = tmp; $$ = tmp;
} }
| hierarchy_identifier '(' expression_list_proper ')' ';' | hierarchy_identifier '(' expression_list_with_nuls ')' ';'
{ PCallTask*tmp = new PCallTask(*$1, *$3); { PCallTask*tmp = new PCallTask(*$1, *$3);
FILE_NAME(tmp, @1); FILE_NAME(tmp, @1);
delete $1; delete $1;
@ -5708,7 +5757,7 @@ statement /* This is roughly statement_item in the LRM */
$$ = tmp; $$ = tmp;
} }
| implicit_class_handle '.' hierarchy_identifier '(' expression_list_proper ')' ';' | implicit_class_handle '.' hierarchy_identifier '(' expression_list_with_nuls ')' ';'
{ PCallTask*tmp = new PCallTask(*$3, *$5); { PCallTask*tmp = new PCallTask(*$3, *$5);
yyerror(@1, "sorry: Implicit class handle not supported in front of task names."); yyerror(@1, "sorry: Implicit class handle not supported in front of task names.");
FILE_NAME(tmp, @1); FILE_NAME(tmp, @1);
@ -5717,28 +5766,6 @@ statement /* This is roughly statement_item in the LRM */
$$ = tmp; $$ = tmp;
} }
/* NOTE: The standard doesn't really support an empty argument list
between parentheses, but it seems natural, and people commonly
want it. So accept it explicitly. */
| hierarchy_identifier '(' ')' ';'
{ list<PExpr*>pt;
PCallTask*tmp = new PCallTask(*$1, pt);
FILE_NAME(tmp, @1);
delete $1;
$$ = tmp;
}
| implicit_class_handle '.' hierarchy_identifier '(' ')' ';'
{ list<PExpr*>pt;
yyerror(@1, "sorry: Implicit class handle not supported in front of task names.");
PCallTask*tmp = new PCallTask(*$3, pt);
FILE_NAME(tmp, @3);
delete $3;
$$ = tmp;
}
| hierarchy_identifier ';' | hierarchy_identifier ';'
{ list<PExpr*>pt; { list<PExpr*>pt;
PCallTask*tmp = new PCallTask(*$1, pt); PCallTask*tmp = new PCallTask(*$1, pt);
@ -5746,11 +5773,22 @@ statement /* This is roughly statement_item in the LRM */
delete $1; delete $1;
$$ = tmp; $$ = tmp;
} }
| hierarchy_identifier '(' error ')' ';'
{ yyerror(@3, "error: Syntax error in task arguments.");
list<PExpr*>pt;
PCallTask*tmp = new PCallTask(*$1, pt);
FILE_NAME(tmp, @1);
delete $1;
$$ = tmp;
}
| error ';' | error ';'
{ yyerror(@2, "error: malformed statement"); { yyerror(@2, "error: malformed statement");
yyerrok; yyerrok;
$$ = new PNoop; $$ = new PNoop;
} }
; ;
compressed_statement compressed_statement
@ -5841,45 +5879,7 @@ analog_statement
other block items. */ other block items. */
task_item task_item
: block_item_decl { $$ = new svector<PWire*>(0); } : block_item_decl { $$ = new svector<PWire*>(0); }
| task_port_item { $$ = $1; } | tf_port_declaration { $$ = $1; }
;
task_port_item
: port_direction K_reg_opt unsigned_signed_opt range_opt list_of_identifiers ';'
{ svector<PWire*>*tmp = pform_make_task_ports(@1, $1,
$2 ? IVL_VT_LOGIC :
IVL_VT_NO_TYPE,
$3, $4, $5);
$$ = tmp;
}
/* When the port is an integer, infer a signed vector of the integer
shape. Generate a range ([31:0]) to make it work. */
| port_direction K_integer list_of_identifiers ';'
{ list<index_component_t>*range_stub = make_range_from_width(integer_width);
svector<PWire*>*tmp = pform_make_task_ports(@1, $1, IVL_VT_LOGIC, true,
range_stub, $3, true);
$$ = tmp;
}
/* Ports can be time with a width of [63:0] (unsigned). */
| port_direction K_time list_of_identifiers ';'
{ list<index_component_t>*range_stub = make_range_from_width(64);
svector<PWire*>*tmp = pform_make_task_ports(@1, $1, IVL_VT_LOGIC, false,
range_stub, $3);
$$ = tmp;
}
/* Ports can be real or realtime. */
| port_direction real_or_realtime list_of_identifiers ';'
{ svector<PWire*>*tmp = pform_make_task_ports(@1, $1, IVL_VT_REAL, false,
0, $3);
$$ = tmp;
}
; ;
task_item_list task_item_list