From bdab7698d2bb7029a3d210d0b8918b967ed4db9c Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Fri, 18 Apr 2008 21:33:03 -0700 Subject: [PATCH] User defined functions can have ANSI-style ports Update the rules for parsing user function definitions to allow Verilog-2001 ANSI style port declarations. In the process, also unify with the user task port declaration so that the types don't diverge. The rules are the same for both, with the extra constraint that function ports must all be input. This latter rule is checked later, during elaboration, so that the task/function pform code can be shared, and better error messages can be generated. --- elab_sig.cc | 20 +++++++++++ parse.y | 101 +++++++++++++++------------------------------------- 2 files changed, 49 insertions(+), 72 deletions(-) diff --git a/elab_sig.cc b/elab_sig.cc index 889819563..7c73c718d 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -593,6 +593,8 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const perm_string pname = (*ports_)[idx]->basename(); NetNet*tmp = scope->find_signal(pname); + ports[idx] = 0; + if (tmp == 0) { cerr << get_fileline() << ": internal error: function " << scope_path(scope) << " is missing port " @@ -600,9 +602,27 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const scope->dump(cerr); cerr << get_fileline() << ": Continuing..." << endl; des->errors += 1; + continue; + } + + if (tmp->port_type() == NetNet::NOT_A_PORT) { + cerr << get_fileline() << ": internal error: function " + << scope_path(scope) << " port " << pname + << " is a port but is not a port?" << endl; + des->errors += 1; + scope->dump(cerr); + continue; } ports[idx] = tmp; + if (tmp->port_type() != NetNet::PINPUT) { + cerr << tmp->get_fileline() << ": error: function " + << scope_path(scope) << " port " << pname + << " is not an input port." << endl; + cerr << tmp->get_fileline() << ": : Function arguments " + << "must be input ports." << endl; + des->errors += 1; + } } diff --git a/parse.y b/parse.y index b3a1a3377..abe7af828 100644 --- a/parse.y +++ b/parse.y @@ -1097,76 +1097,9 @@ expr_primary } ; - /* A function_item is either a block item (i.e. a reg or integer - declaration) or an input declaration. There are no output or - inout ports. */ -function_item - : K_input signed_opt range_opt list_of_identifiers ';' - { svector*tmp - = pform_make_task_ports(NetNet::PINPUT, - IVL_VT_NO_TYPE, $2, - $3, $4, - @1.text, @1.first_line); - $$ = tmp; - } - | K_output signed_opt range_opt list_of_identifiers ';' - { svector*tmp - = pform_make_task_ports(NetNet::PINPUT, - IVL_VT_NO_TYPE, $2, - $3, $4, - @1.text, @1.first_line); - $$ = tmp; - yyerror(@1, "Functions may not have output ports."); - } - | K_inout signed_opt range_opt list_of_identifiers ';' - { svector*tmp - = pform_make_task_ports(NetNet::PINPUT, - IVL_VT_NO_TYPE, $2, - $3, $4, - @1.text, @1.first_line); - $$ = tmp; - yyerror(@1, "Functions may not have inout ports."); - } - - /* When the port is an integer, infer a signed vector of the integer - shape. Generate a range to make it work. */ - - | K_input K_integer list_of_identifiers ';' - { svector*range_stub - = new svector(2); - PExpr*re; - re = new PENumber(new verinum(integer_width-1, - integer_width)); - (*range_stub)[0] = re; - re = new PENumber(new verinum((uint64_t)0, integer_width)); - (*range_stub)[1] = re; - svector*tmp - = pform_make_task_ports(NetNet::PINPUT, - IVL_VT_LOGIC, true, - range_stub, $3, - @1.text, @1.first_line); - $$ = tmp; - } - - /* Ports can be real. */ - - | K_input K_real list_of_identifiers ';' - { svector*tmp - = pform_make_task_ports(NetNet::PINPUT, - IVL_VT_REAL, false, - 0, $3, - @1.text, @1.first_line); - $$ = tmp; - } - - | block_item_decl - { $$ = 0; } - ; - - /* A function_item_list only lists the input/output/inout - declarations. The integer and reg declarations are handled in - place, so are not listed. The list builder needs to account for - the possibility that the various parts may be NULL. */ + /* A function_item_list borrows the task_port_item run to match + declarations of ports. We check later to make sure there are no + output or inout ports actually used. */ function_item_list : function_item { $$ = $1; } @@ -1184,6 +1117,13 @@ function_item_list } ; +function_item + : task_port_item + { $$ = $1; } + | block_item_decl + { $$ = 0; } + ; + /* A gate_instance is a module instantiation or a built in part type. In any case, the gate has a set of connections to ports. */ gate_instance @@ -1865,7 +1805,7 @@ module_item FILE_NAME(current_task, @1); } '(' task_port_decl_list ')' ';' - task_item_list_opt + block_item_decls_opt statement_or_null K_endtask { current_task->set_ports($5); @@ -1895,6 +1835,23 @@ module_item delete[]$3; } + | K_function function_range_or_type_opt IDENTIFIER + { assert(current_function == 0); + current_function = pform_push_function_scope($3); + FILE_NAME(current_function, @1); + } + '(' task_port_decl_list ')' ';' + block_item_decls_opt + statement + K_endfunction + { current_function->set_ports($6); + current_function->set_statement($10); + current_function->set_return($2); + pform_pop_scope(); + current_function = 0; + delete[]$3; + } + /* A generate region can contain further module items. Actually, it is supposed to be limited to certain kinds of module items, but the semantic tests will check that for us. */ @@ -3312,7 +3269,7 @@ task_port_item : K_input signed_opt range_opt list_of_identifiers ';' { svector*tmp = pform_make_task_ports(NetNet::PINPUT, - IVL_VT_LOGIC, $2, + IVL_VT_NO_TYPE, $2, $3, $4, @1.text, @1.first_line); $$ = tmp;