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:
parent
da743c3b2c
commit
dbc6f0cff2
12
Statement.h
12
Statement.h
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
45
elaborate.cc
45
elaborate.cc
|
|
@ -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);
|
||||||
|
|
@ -2985,7 +3012,7 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const
|
||||||
unsigned wid = count_lval_width(lv);
|
unsigned wid = count_lval_width(lv);
|
||||||
ivl_variable_type_t lv_type = lv->expr_type();
|
ivl_variable_type_t lv_type = lv->expr_type();
|
||||||
|
|
||||||
NetExpr*rv = elaborate_rval_expr(des, scope, lv_type, wid, parms_[idx]);
|
NetExpr*rv = elaborate_rval_expr(des, scope, lv_type, wid, parms_ [idx]);
|
||||||
if (NetEEvent*evt = dynamic_cast<NetEEvent*> (rv)) {
|
if (NetEEvent*evt = dynamic_cast<NetEEvent*> (rv)) {
|
||||||
cerr << evt->get_fileline() << ": error: An event '"
|
cerr << evt->get_fileline() << ": error: An event '"
|
||||||
<< evt->event()->name() << "' can not be a user "
|
<< evt->event()->name() << "' can not be a user "
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
170
parse.y
170
parse.y
|
|
@ -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,26 +3112,34 @@ 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) {
|
{ /* */
|
||||||
svector<PWire*>*tmp = new svector<PWire*>(*$1, *$2);
|
if ($1 && $2) {
|
||||||
delete $1;
|
svector<PWire*>*tmp = new svector<PWire*>(*$1, *$2);
|
||||||
delete $2;
|
delete $1;
|
||||||
$$ = tmp;
|
delete $2;
|
||||||
} else if ($1) {
|
$$ = tmp;
|
||||||
$$ = $1;
|
} else if ($1) {
|
||||||
} else {
|
$$ = $1;
|
||||||
$$ = $2;
|
} else {
|
||||||
}
|
$$ = $2;
|
||||||
}
|
}
|
||||||
;
|
}
|
||||||
|
;
|
||||||
|
|
||||||
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,11 +5766,7 @@ statement /* This is roughly statement_item in the LRM */
|
||||||
$$ = tmp;
|
$$ = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NOTE: The standard doesn't really support an empty argument list
|
| hierarchy_identifier ';'
|
||||||
between parentheses, but it seems natural, and people commonly
|
|
||||||
want it. So accept it explicitly. */
|
|
||||||
|
|
||||||
| hierarchy_identifier '(' ')' ';'
|
|
||||||
{ list<PExpr*>pt;
|
{ list<PExpr*>pt;
|
||||||
PCallTask*tmp = new PCallTask(*$1, pt);
|
PCallTask*tmp = new PCallTask(*$1, pt);
|
||||||
FILE_NAME(tmp, @1);
|
FILE_NAME(tmp, @1);
|
||||||
|
|
@ -5729,29 +5774,22 @@ statement /* This is roughly statement_item in the LRM */
|
||||||
$$ = tmp;
|
$$ = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
| hierarchy_identifier '(' error ')' ';'
|
||||||
| implicit_class_handle '.' hierarchy_identifier '(' ')' ';'
|
{ yyerror(@3, "error: Syntax error in task arguments.");
|
||||||
{ list<PExpr*>pt;
|
list<PExpr*>pt;
|
||||||
yyerror(@1, "sorry: Implicit class handle not supported in front of task names.");
|
PCallTask*tmp = new PCallTask(*$1, pt);
|
||||||
PCallTask*tmp = new PCallTask(*$3, pt);
|
FILE_NAME(tmp, @1);
|
||||||
FILE_NAME(tmp, @3);
|
delete $1;
|
||||||
delete $3;
|
|
||||||
$$ = tmp;
|
$$ = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
| hierarchy_identifier ';'
|
| error ';'
|
||||||
{ list<PExpr*>pt;
|
{ yyerror(@2, "error: malformed statement");
|
||||||
PCallTask*tmp = new PCallTask(*$1, pt);
|
yyerrok;
|
||||||
FILE_NAME(tmp, @1);
|
$$ = new PNoop;
|
||||||
delete $1;
|
}
|
||||||
$$ = tmp;
|
|
||||||
}
|
;
|
||||||
| error ';'
|
|
||||||
{ yyerror(@2, "error: malformed statement");
|
|
||||||
yyerrok;
|
|
||||||
$$ = new PNoop;
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
compressed_statement
|
compressed_statement
|
||||||
: lpvalue K_PLUS_EQ expression
|
: lpvalue K_PLUS_EQ expression
|
||||||
|
|
@ -5841,47 +5879,9 @@ 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
|
||||||
: task_item_list task_item
|
: task_item_list task_item
|
||||||
{ svector<PWire*>*tmp = new svector<PWire*>(*$1, *$2);
|
{ svector<PWire*>*tmp = new svector<PWire*>(*$1, *$2);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue