From 38b3c8efb26ff4f5e0018205b17f322de3f2778b Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Thu, 31 Dec 2020 13:04:56 -0800 Subject: [PATCH] Rework symbol_search function. There are too many ad hoc handlers of symbol_search partial results. Rewrite symbol_search to clean up things like partial results and member/method detections. Use this reworked symbol_search function to rewrite expression elaborate for the PECallFunction expressions. --- Makefile.in | 1 + PExpr.h | 17 +- elab_expr.cc | 1073 ++++++++++++++++++++++++++++++---------------- elab_sig.cc | 40 +- elaborate.cc | 3 +- make_ivl_type.cc | 48 +++ netmisc.h | 87 ++++ nettypes.h | 7 + pform_types.h | 6 +- symbol_search.cc | 258 +++++++---- 10 files changed, 1059 insertions(+), 481 deletions(-) create mode 100644 make_ivl_type.cc diff --git a/Makefile.in b/Makefile.in index 1d84a3924..62062f65c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -111,6 +111,7 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ load_module.o netlist.o netmisc.o nettypes.o net_analog.o net_assign.o \ net_design.o netclass.o netdarray.o \ netenum.o netparray.o netqueue.o netscalar.o netstruct.o netvector.o \ + make_ivl_type.o \ net_event.o net_expr.o net_func.o \ net_func_eval.o net_link.o net_modulo.o \ net_nex_input.o net_nex_output.o net_proc.o net_scope.o net_tran.o \ diff --git a/PExpr.h b/PExpr.h index fe05c8c62..627e3183d 100644 --- a/PExpr.h +++ b/PExpr.h @@ -35,6 +35,7 @@ class NetNet; class NetExpr; class NetScope; class PPackage; +struct symbol_search_results; /* * The PExpr class hierarchy supports the description of @@ -932,14 +933,13 @@ class PECallFunction : public PExpr { NetExpr*elaborate_expr_pkg_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags)const; - NetExpr*elaborate_expr_method_(Design*des, NetScope*scope, - unsigned expr_wid, - bool add_this_flag = false) const; - NetExpr*elaborate_expr_method_net_(Design*des, NetScope*scope, - NetNet*net, unsigned expr_wid) const; - NetExpr*elaborate_expr_method_par_(Design*des, NetScope*scope, - const NetExpr *par, ivl_type_t par_type, - unsigned expr_wid) const; + + NetExpr* elaborate_expr_method_(Design*des, NetScope*scope, + symbol_search_results&search_results, + unsigned expr_wid) const; + NetExpr* elaborate_expr_method_par_(Design*des, NetScope*scope, + symbol_search_results&search_results, + unsigned expr_wid) const; NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, @@ -950,6 +950,7 @@ class PECallFunction : public PExpr { unsigned test_width_sfunc_(Design*des, NetScope*scope, width_mode_t&mode); unsigned test_width_method_(Design*des, NetScope*scope, + symbol_search_results&search_results, width_mode_t&mode); NetExpr*elaborate_base_(Design*des, NetScope*scope, NetScope*dscope, diff --git a/elab_expr.cc b/elab_expr.cc index de82f31df..9515fb18c 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -621,7 +621,7 @@ unsigned PEBComp::test_width(Design*des, NetScope*scope, width_mode_t&) } if (debug_elaborate) { - cerr << get_fileline() << ": debug: " + cerr << get_fileline() << ": PEBComp::test_width: " << "Comparison expression operands are " << l_type << " " << l_width << " bits and " << r_type << " " << r_width << " bits. Resorting to " @@ -668,6 +668,17 @@ NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, ivl_assert(*this, left_); ivl_assert(*this, right_); + if (debug_elaborate) { + cerr << get_fileline() << ": PEBComp::elaborate_expr: " + << "Left expression: " << *left_ << endl; + cerr << get_fileline() << ": PEBComp::elaborate_expr: " + << "Right expression: " << *right_ << endl; + cerr << get_fileline() << ": PEBComp::elaborate_expr: " + << "op_: " << human_readable_op(op_) + << ", expr_wid=" << expr_wid + << ", flags=0x" << hex << flags << dec << endl; + } + // Propagate the comparison type (signed/unsigned) down to // the operands. if (type_is_vectorable(left_->expr_type()) && !left_->has_sign()) @@ -676,7 +687,16 @@ NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, left_->cast_signed(false); NetExpr*lp = left_->elaborate_expr(des, scope, l_width_, flags); + if (lp && debug_elaborate) { + cerr << get_fileline() << ": PEBComp::elaborate_expr: " + << "Elaborated left_: " << *lp << endl; + } NetExpr*rp = right_->elaborate_expr(des, scope, r_width_, flags); + if (rp && debug_elaborate) { + cerr << get_fileline() << ": PEBComp::elaborate_expr: " + << "Elaborated right_: " << *rp << endl; + } + if ((lp == 0) || (rp == 0)) { delete lp; delete rp; @@ -1255,152 +1275,149 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, return expr_width_; } -unsigned PECallFunction::test_width(Design*des, NetScope*scope, - width_mode_t&mode) +/* + * Get the function definition from the scope that we believe to be a + * function. If it is not, return 0. If it is, handle the special case that we + * may be still elaborating things. For example: + * + * localparam foo = func(...) + * + * In this case, the function is not necessarily elaborated yet, and we need + * to force enough elaboration that we can get a definition. + */ +static NetFuncDef* find_function_definition(Design*des, NetScope*scope, + NetScope*func) { - if (peek_tail_name(path_)[0] == '$') - return test_width_sfunc_(des, scope, mode); - - // The width of user defined functions depends only on the - // width of the return value. The arguments are entirely - // self-determined. - NetFuncDef*def = des->find_function(scope, path_); - if (def == 0) { - // If this is an access function, then the width and - // type are known by definition. - if (find_access_function(path_)) { - expr_type_ = IVL_VT_REAL; - expr_width_ = 1; - min_width_ = 1; - signed_flag_ = true; - - return expr_width_; + if (func && (func->type() == NetScope::FUNC)) { + if (func->elab_stage() < 2) { + func->need_const_func(true); + const PFunction*pfunc = func->func_pform(); + assert(pfunc); + pfunc->elaborate_sig(des, func); } - - if (test_width_method_(des, scope, mode)) { - if (debug_elaborate) - cerr << get_fileline() << ": PECallFunction::" << __func__ << ": " - << "test_width of method returns width " << expr_width_ - << ", type=" << expr_type_ - << "." << endl; - return expr_width_; - } - - if (debug_elaborate) - cerr << get_fileline() << ": PECallFunction::" << __func__ << ": " - << "test_width cannot find definition of " << path_ - << " in " << scope_path(scope) << "." << endl; - return 0; + return func->func_def(); } - - NetScope*dscope = def->scope(); - assert(dscope); - - if (NetNet*res = dscope->find_signal(dscope->basename())) { - expr_type_ = res->data_type(); - expr_width_ = res->vector_width(); - min_width_ = expr_width_; - signed_flag_ = res->get_signed(); - - if (debug_elaborate) - cerr << get_fileline() << ": " << __func__ << ": " - << "test_width of function returns width " << expr_width_ - << ", type=" << expr_type_ - << "." << endl; - - return expr_width_; - } - - ivl_assert(*this, 0); return 0; } unsigned PECallFunction::test_width_method_(Design*des, NetScope*scope, - width_mode_t&) + symbol_search_results&search_results, + width_mode_t&mode) { if (!gn_system_verilog()) return 0; - // This is only useful if the path is at least 2 elements. For - // example, foo.bar() is a method, bar() is not. - if (path_.size() < 2) - return 0; - - perm_string member_name; - pform_name_t use_path = path_; - perm_string method_name = peek_tail_name(use_path); - use_path.pop_back(); - if (debug_elaborate) { - cerr << get_fileline() << ": " << __func__ << ": " - << "use_path=" << use_path - << ", method_name=" << method_name - << endl; + cerr << get_fileline() << ": PECallFunction::test_width_method_: " + << "search_results.path_item: " << search_results.path_item << endl; + cerr << get_fileline() << ": PECallFunction::test_width_method_: " + << "search_results.path_tail: " << search_results.path_tail << endl; + if (search_results.net) + cerr << get_fileline() << ": PECallFunction::test_width_method_: " + << "search_results.net->data_type: " << search_results.net->data_type() << endl; + if (search_results.net && search_results.net->net_type()) + cerr << get_fileline() << ": PECallFunction::test_width_method_: " + << "search_results.net->net_type: " << *search_results.net->net_type() << endl; } - NetNet *net = 0; - const NetExpr *par; - ivl_type_t par_type = 0; - NetEvent *eve; - - symbol_search(this, des, scope, use_path, net, par, eve, par_type); - - if (debug_elaborate && net!=0) { - cerr << get_fileline() << ": " << __func__ << ": " - << "net=" << net->name() << endl; - cerr << get_fileline() << ": " << __func__ << ": " - << "net->data_type()=" << net->data_type() << endl; - if (net->net_type()) - cerr << get_fileline() << ": " << __func__ << ": " - << "net->net_type()=" << *net->net_type() << endl; - } - - const netdarray_t*use_darray = 0; - - if (net != 0) - use_darray = net->darray_type(); - - // Net is not found, but maybe it is a member of a - // struct or class. Try to locate net without the member - // name and test if it is a type that has members. - if (net == 0 && use_path.size() >= 2) { - pform_name_t tmp_path = use_path; - member_name = peek_tail_name(tmp_path); - tmp_path.pop_back(); - - net = 0; - symbol_search(this, des, scope, tmp_path, - net, par, eve, par_type); - if (net && net->class_type()) { - if (debug_elaborate) { - cerr << get_fileline() << ": PECallFunction::test_width_method_: " - << "Found net=" << tmp_path - << ", member_name=" << member_name - << ", method_name=" << method_name - << endl; - } - - const netclass_t* class_type = net->class_type(); - int midx = class_type->property_idx_from_name(member_name); - ivl_type_t member_type = 0; - if (midx >= 0) member_type = class_type->get_prop_type(midx); - - use_darray = dynamic_cast (member_type); - - } else { - member_name = perm_string(); - net = 0; + // Don't support multiple chained methods yet. + if (search_results.path_tail.size() > 1) { + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::test_width_method_: " + << "Chained path tail (" << search_results.path_tail + << ") not supported." << endl; } + return 0; } - // After all, no sign of a net match. Give up. - if (net == 0) + ivl_assert(*this, search_results.path_tail.size() == 1); + perm_string method_name = search_results.path_tail.back().name; + + // Dynamic array variable without a select expression. The method + // applies to the array itself, and not to the object that might be + // indexed from it. So return + // the expr_width for the return value of the queue method. For example: + // .x.size(); + // In this example, x is a dynamic array. + if (search_results.net && search_results.net->data_type()==IVL_VT_DARRAY + && search_results.path_item.index.empty()) { + + NetNet*net = search_results.net; + const netdarray_t*darray = net->darray_type(); + ivl_assert(*this, darray); + + if (method_name == "size") { + expr_type_ = IVL_VT_BOOL; + expr_width_ = 32; + min_width_ = expr_width_; + signed_flag_= true; + return expr_width_; + } + return 0; + } - // Look for built in string attributes. - if (net->data_type()==IVL_VT_STRING) { + // Queue variable without a select expression. The method applies to the + // queue, and not to the object that might be indexed from it. So return + // the expr_width for the return value of the queue method. For example: + // .x.size(); + // In this example, x is a queue. + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE + && search_results.path_item.index.empty()) { - if (method_name == "len") { + NetNet*net = search_results.net; + const netdarray_t*darray = net->darray_type(); + ivl_assert(*this, darray); + + if (method_name == "size") { + expr_type_ = IVL_VT_BOOL; + expr_width_ = 32; + min_width_ = expr_width_; + signed_flag_= true; + return expr_width_; + } + + if (method_name=="pop_back" || method_name=="pop_front") { + expr_type_ = darray->element_base_type(); + expr_width_ = darray->element_width(); + min_width_ = expr_width_; + signed_flag_= darray->get_signed(); + return expr_width_; + } + + return 0; + } + + // Queue variable with a select expression. The type of this expression + // is the type of the object that will interpret the method. For + // example: + // .x[e].len() + // If for example x is a queue of strings, then x[e] is a string and + // x[e].len() is the length of the string. + if (search_results.net + && (search_results.net->data_type()==IVL_VT_QUEUE || search_results.net->data_type()==IVL_VT_DARRAY) + && search_results.path_item.index.size()) { + + NetNet*net = search_results.net; + const netdarray_t*darray = net->darray_type(); + ivl_assert(*this, darray); + + if (darray->element_base_type()==IVL_VT_STRING && method_name=="atohex") { + expr_type_ = IVL_VT_BOOL; + expr_width_ = integer_width; + min_width_ = integer_width; + signed_flag_ = true; + return expr_width_; + } + + if (darray->element_base_type()==IVL_VT_STRING && method_name=="atoi") { + expr_type_ = IVL_VT_BOOL; + expr_width_ = integer_width; + min_width_ = integer_width; + return expr_width_; + } + + if (darray->element_base_type()==IVL_VT_STRING && method_name=="len") { expr_type_ = IVL_VT_BOOL; expr_width_ = 32; min_width_ = 32; @@ -1409,38 +1426,11 @@ unsigned PECallFunction::test_width_method_(Design*des, NetScope*scope, } } - // function int size() - if (use_darray && method_name == "size") { - if (debug_elaborate) { - cerr << get_fileline() << ": PECallFunction::test_width_method_: " - << "Match darray size() method." << endl; - } + // Enumeration variable. Check for the various enumeration methods. + if (search_results.net && search_results.net->enumeration()) { + NetNet*net = search_results.net; + const netenum_t*enum_type = net->enumeration(); - expr_type_ = IVL_VT_BOOL; - expr_width_ = 32; - min_width_ = expr_width_; - signed_flag_= true; - return expr_width_; - } - - if (use_darray && (method_name == "pop_back" || method_name=="pop_front")) { - if (debug_elaborate) { - cerr << get_fileline() << ": PECallFunction::test_width_method_: " - << "Detected " << method_name << " method" - << " of dynamic arrays." << endl; - } - - expr_type_ = use_darray->element_base_type(); - expr_width_ = use_darray->element_width(); - min_width_ = expr_width_; - signed_flag_= false; - - return expr_width_; - } - - // If the net is an enumeration, and the method is one of the standard - // methods, then we know the size. - if (const netenum_t*enum_type = net->enumeration()) { if (method_name=="first" || method_name=="last" || method_name=="prev" || method_name=="next") { expr_type_ = IVL_VT_BOOL; @@ -1463,34 +1453,194 @@ unsigned PECallFunction::test_width_method_(Design*des, NetScope*scope, signed_flag_= false; return expr_width_; } + return 0; } - if (const netclass_t*class_type = net->class_type()) { - if (debug_elaborate) { - cerr << get_fileline() << ": PECallFunction::test_width_method_: " - << "Try to find method " << method_name - << " of class " << class_type->get_name() << endl; - } + // Class variables. In this case, the search found the class instance, + // and the scope is the scope where the instance lives. The class method + // in turn defines it's own scope. Use that to find the return value. + if (search_results.net && search_results.net->data_type()==IVL_VT_CLASS) { + NetNet*net = search_results.net; + const netclass_t*class_type = net->class_type(); + ivl_assert(*this, class_type); + NetScope*method = class_type->method_from_name(method_name); - NetScope*func = class_type->method_from_name(method_name); - if (func == 0) { + if (method == 0) { return 0; } - // Get the function result size be getting the details - // from the variable in the function scope that has the - // name of the function. - if (NetNet*res = func->find_signal(method_name)) { - expr_type_ = res->data_type(); - expr_width_= res->vector_width(); - min_width_ = expr_width_; - signed_flag_ = res->get_signed(); - return expr_width_; - } else { - ivl_assert(*this, 0); + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::test_width_method_: " + << "Found method " << scope_path(method) << "(...)" << endl; } + + // Get the return value of the method function. + if (NetNet*res = method->find_signal(method->basename())) { + expr_type_ = res->data_type(); + expr_width_ = res->vector_width(); + min_width_ = expr_width_; + signed_flag_ = res->get_signed(); + + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::test_width_method_: " + << "test_width of class method returns width " << expr_width_ + << ", type=" << expr_type_ + << "." << endl; + } + return expr_width_; + } + return 0; } + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::test_width_method_: " + << "I give up." << endl; + } + return 0; +} + +unsigned PECallFunction::test_width(Design*des, NetScope*scope, + width_mode_t&mode) +{ + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::test_width: " + << "path_: " << path_ << endl; + cerr << get_fileline() << ": PECallFunction::test_width: " + << "mode: " << width_mode_name(mode) << endl; + } + + if (peek_tail_name(path_)[0] == '$') + return test_width_sfunc_(des, scope, mode); + + // Search for the symbol. This should turn up a scope. + symbol_search_results search_results; + bool search_flag = symbol_search(this, des, scope, path_, &search_results); + + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::test_width: " + << "search_flag: " << (search_flag? "true" : "false") << endl; + if (search_results.scope) + cerr << get_fileline() << ": PECallFunction::test_width: " + << "search_results.scope: " << scope_path(search_results.scope) << endl; + if (search_results.net) + cerr << get_fileline() << ": PECallFunction::test_width: " + << "search_results.net: " << search_results.net->name() << endl; + cerr << get_fileline() << ": PECallFunction::test_width: " + << "search_results.path_item: " << search_results.path_item << endl; + cerr << get_fileline() << ": PECallFunction::test_width: " + << "search_results.path_tail: " << search_results.path_tail << endl; + } + + // Nothing found? Return nothing. + if (!search_flag) { + expr_width_ = 0; + min_width_ = 0; + signed_flag_ = false; + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::test_width: " + << "Not found, returning nil width results." << endl; + } + return expr_width_; + } + + // Catch the special case that this is not a scope, but that we + // are in fact in a function calling ourself recursively. For + // example: + // + // function integer factoral; + // input integer n; + // begin + // if (n > 1) + // factorial = n * factorial(n-1); <== HERE + // else + // factorial = n; + // end + // endfunction + // + // In this case, the call to factorial within itself will find the + // net "factorial", but we can notice that the scope is a function + // with the same name as the function. + if (test_function_return_value(search_results)) { + + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::test_width: " + << "Net " << search_results.net->name() + << " is actually a function call to " << scope_path(search_results.scope) + << "." << endl; + } + + NetNet*res = search_results.net; + expr_type_ = res->data_type(); + expr_width_ = res->vector_width(); + min_width_ = expr_width_; + signed_flag_ = res->get_signed(); + + if (debug_elaborate) + cerr << get_fileline() << ": PECallFunction::test_width: " + << "test_width of function returns width " << dec << expr_width_ + << ", type=" << expr_type_ + << "." << endl; + + return expr_width_; + + } + + // If the symbol is found, but is not a scope... + if (!search_results.is_scope()) { + + if (!search_results.path_tail.empty()) { + return test_width_method_(des, scope, search_results, mode); + } + + // I don't know what to do about this. + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::test_width: " + << "I don't know how to handle non-scopes here." << endl; + } + return 0; + } + + + NetFuncDef*def = find_function_definition(des, scope, search_results.scope); + if (def == 0) { + // If this is an access function, then the width and + // type are known by definition. + if (find_access_function(path_)) { + expr_type_ = IVL_VT_REAL; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; + + return expr_width_; + } + + // I don't know what to do about this. + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::test_width: " + << "Scope is not a function." << endl; + } + return 0; + } + + NetScope*dscope = def->scope(); + assert(dscope); + + if (NetNet*res = dscope->find_signal(dscope->basename())) { + expr_type_ = res->data_type(); + expr_width_ = res->vector_width(); + min_width_ = expr_width_; + signed_flag_ = res->get_signed(); + + if (debug_elaborate) + cerr << get_fileline() << ": PECallFunction::test_width: " + << "test_width of function returns width " << expr_width_ + << ", type=" << expr_type_ + << "." << endl; + + return expr_width_; + } + + ivl_assert(*this, 0); return 0; } @@ -2429,6 +2579,17 @@ NetExpr* PECallFunction::elaborate_expr_pkg_(Design*des, NetScope*scope, NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const { + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "path_: " << path_ << endl; + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "expr_wid: " << expr_wid << endl; + if (package_) + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "package_: " << package_->pscope_name() + << " at " << package_->get_fileline() << endl; + } + if (package_) return elaborate_expr_pkg_(des, scope, expr_wid, flags); @@ -2437,7 +2598,69 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, if (peek_tail_name(path_)[0] == '$') return elaborate_sfunc_(des, scope, expr_wid, flags); - NetFuncDef*def = des->find_function(scope, path_); + // Search for the symbol. This should turn up a scope. + symbol_search_results search_results; + bool search_flag = symbol_search(this, des, scope, path_, &search_results); + + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "search_flag: " << (search_flag? "true" : "false") << endl; + if (search_results.scope) + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "search_results.scope: " << scope_path(search_results.scope) << endl; + if (search_results.net) + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "search_results.net: " << search_results.net->name() << endl; + if (search_results.par_val) + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "search_results.par_val: " << *search_results.par_val << endl; + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "search_results.path_item: " << search_results.path_item << endl; + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "search_results.path_tail: " << search_results.path_tail << endl; + } + + // If the symbol is not found at all... + if (!search_flag) { + cerr << get_fileline() << ": error: No function named `" << path_ + << "' found in this context (" << scope_path(scope) << ")." + << endl; + des->errors += 1; + return 0; + } + + // If the symbol is found, but is not a scope... + if (! search_results.is_scope() && !test_function_return_value(search_results)) { + + // Maybe this is a method of an object? Give it a try. + if (!search_results.path_tail.empty()) { + NetExpr*tmp = elaborate_expr_method_(des, scope, search_results, expr_wid); + if (tmp) { + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "Elaborated method: " << *tmp << endl; + } + return tmp; + } else { + cerr << get_fileline() << ": error: " + << "Object " << scope_path(search_results.scope) + << "." << search_results.path_item + << " has no method \"" << search_results.path_tail + << "(...)\"." << endl; + des->errors += 1; + return 0; + } + } + + cerr << get_fileline() << ": error: Object " << search_results.path_item + << " in " << scope_path(search_results.scope) + << " is not a function." << endl; + des->errors += 1; + return 0; + } + + // If the symbol is found, but is not a _function_ scope... + NetFuncDef*def = search_results.scope->func_def(); if (def == 0) { // Not a user defined function. Maybe it is an access // function for a nature? If so then elaborate it that @@ -2447,13 +2670,6 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, return elaborate_access_func_(des, scope, access_nature, expr_wid); - // Maybe this is a method attached to a signal? If this - // is SystemVerilog then try that possibility. - if (gn_system_verilog()) { - NetExpr*tmp = elaborate_expr_method_(des, scope, expr_wid); - if (tmp) return tmp; - } - // Nothing was found so report this as an error. cerr << get_fileline() << ": error: No function named `" << path_ << "' found in this context (" << scope_path(scope) << ")." @@ -2463,18 +2679,33 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, } ivl_assert(*this, def); - NetScope*dscope = def->scope(); - ivl_assert(*this, dscope); + ivl_assert(*this, def->scope() == search_results.scope); + NetScope*dscope = search_results.scope; - /* In SystemVerilog a method calling another method in the - * current class needs to be elaborated as a method with an - * implicit this added. */ + // In SystemVerilog, a method calling another method in the current + // class needs to be elaborated as a method with an implicit "this" + // added. This is a special case. If we detect this case, then + // synthesize a new symbol_search_results thast properly reflects the + // implicit "this", and treat this item as a class method. if (gn_system_verilog() && (path_.size() == 1)) { const NetScope *c_scope = scope->get_class_scope(); if (c_scope && (c_scope == dscope->get_class_scope())) { - NetExpr*tmp = elaborate_expr_method_(des, scope, expr_wid, - true); - assert(tmp); + if (debug_elaborate) { + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "Found a class method calling another method." << endl; + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "scope: " << scope_path(scope) << endl; + cerr << get_fileline() << ": PECallFunction::elaborate_expr: " + << "c_scope: " << scope_path(c_scope) << endl; + } + symbol_search_results use_search_results; + use_search_results.scope = scope; + use_search_results.path_tail.push_back(search_results.path_item); + use_search_results.path_item = name_component_t(perm_string::literal(THIS_TOKEN)); + use_search_results.net = scope->find_signal(perm_string::literal(THIS_TOKEN)); + ivl_assert(*this, use_search_results.net); + + NetExpr*tmp = elaborate_expr_method_(des, scope, use_search_results, expr_wid); return tmp; } } @@ -2695,100 +2926,268 @@ unsigned PECallFunction::elaborate_arguments_(Design*des, NetScope*scope, return parm_errors; } +/* + * Look for a method of a given object. The search_results gives us the + * information we need to look into this case: The net is the object that will + * have its method applied, and the path_tail is the method we are looking + * for. The method name is to be interpreted based on the type of the item. So + * for example if the object is: + * + * .x.len() + * + * Then net refers to object named x, and path_item is "x". The method is + * "len" in path_tail, and if x is a string object, we can handle the case. + */ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, - unsigned expr_wid, - bool add_this_flag) const + symbol_search_results&search_results, + unsigned expr_wid) const { - pform_name_t use_path = path_; - perm_string method_name = peek_tail_name(use_path); - use_path.pop_back(); + if (!gn_system_verilog()) { + cerr << get_fileline() << ": error: " + << "Enable SystemVerilog to support object methods." << endl; + des->errors += 1; + return 0; + } - /* Add the implicit this reference when requested. */ - if (add_this_flag) { - assert(use_path.empty()); - use_path.push_front(name_component_t(perm_string::literal(THIS_TOKEN))); + if (search_results.path_tail.size() > 1) { + cerr << get_fileline() << ": sorry: " + << "Method name nesting is not supported yet." << endl; + cerr << get_fileline() << ": : " + << "method path: " << search_results.path_tail << endl; + return 0; } if (debug_elaborate) { - cerr << get_fileline() << ": " << __func__ << ": " - << "use_path: " << use_path << endl; - cerr << get_fileline() << ": " << __func__ << ": " - << "method_name: " << method_name << endl; - cerr << get_fileline() << ": " << __func__ << ": " - << "expr_wid: " << expr_wid << endl; + cerr << get_fileline() << ": PECallFunction::elaborate_expr_method_: " + << "search_results.scope: " << scope_path(search_results.scope) << endl; + cerr << get_fileline() << ": PECallFunction::elaborate_expr_method_: " + << "search_results.path_item: " << search_results.path_item << endl; + cerr << get_fileline() << ": PECallFunction::elaborate_expr_method_: " + << "search_results.path_tail: " << search_results.path_tail << endl; + if (search_results.net) + cerr << get_fileline() << ": PECallFunction::elaborate_expr_method_: " + << "search_results.net->data_type: " << search_results.net->data_type() << endl; + if (search_results.net && search_results.net->net_type()) + cerr << get_fileline() << ": PECallFunction::elaborate_expr_method_: " + << "search_results.net->net_type: " << *search_results.net->net_type() << endl; + if (search_results.par_val) + cerr << get_fileline() << ": PECallFunction::elaborate_expr_method_: " + << "search_results.par_val: " << *search_results.par_val << endl; + if (search_results.par_type) + cerr << get_fileline() << ": PECallFunction::elaborate_expr_method_: " + << "search_results.par_type: " << *search_results.par_type << endl; } - // If there is no object to the left of the method name, then - // give up on the idea of looking for an object method. - if (use_path.empty()) return 0; - - NetNet *net = 0; - const NetExpr *par; - ivl_type_t par_type; - NetEvent *eve; - - symbol_search(this, des, scope, use_path, net, par, eve, par_type); - - if (debug_elaborate) { - if (net) { - cerr << get_fileline() << ": " << __func__ << ": " - << "net: " << net->name() << endl; - } - if (par) { - cerr << get_fileline() << ": " << __func__ << ": " - << "par: " << *par << endl; - } - if (par_type) { - cerr << get_fileline() << ": " << __func__ << ": " - << "par_type: " << *par_type << endl; - } + if (search_results.par_val && search_results.par_type) { + return elaborate_expr_method_par_(des, scope, search_results, expr_wid); } - // If we found a net with a method... - if (net) - return elaborate_expr_method_net_(des, scope, net, expr_wid); + NetExpr* sub_expr = 0; + if (search_results.net) { + NetESignal*tmp = new NetESignal(search_results.net); + tmp->set_line(*this); + sub_expr = tmp; + } - // If we found a parameter with a method... - if (par) - return elaborate_expr_method_par_(des, scope, par, par_type, expr_wid); + // Queue variable with a select expression. The type of this expression + // is the type of the object that will interpret the method. For + // example: + // .x[e].len() + // If x is a queue of strings, then x[e] is a string. Elaborate the x[e] + // expression and pass that to the len() method. + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE + && search_results.path_item.index.size()==1) { - return 0; -} + NetNet*net = search_results.net; + const netdarray_t*darray = net->darray_type(); + const index_component_t&use_index = search_results.path_item.index.back(); + ivl_assert(*this, use_index.msb != 0); + ivl_assert(*this, use_index.lsb == 0); + + NetExpr*mux = elab_and_eval(des, scope, use_index.msb, -1, false); + if (!mux) + return 0; -NetExpr* PECallFunction::elaborate_expr_method_net_(Design*des, NetScope*scope, - NetNet*net, unsigned expr_wid) const -{ - pform_name_t use_path = path_; - perm_string method_name = peek_tail_name(use_path); - use_path.pop_back(); + NetESelect*tmp = new NetESelect(sub_expr, mux, darray->element_width(), darray->element_type()); + tmp->set_line(*this); + sub_expr = tmp; + } - if (net->data_type() == IVL_VT_STRING) { + if (debug_elaborate && sub_expr) { + cerr << get_fileline() << ": PECallFunction::elaborate_expr_method_: " + << "sub_expr->expr_type: " << sub_expr->expr_type() << endl; + if (sub_expr->net_type()) + cerr << get_fileline() << ": PECallFunction::elaborate_expr_method_: " + << "sub_expr->net_type: " << *sub_expr->net_type() << endl; + } + + ivl_assert(*this, sub_expr); + + // Dynamic array methods. This handles the case that the located signal + // is a dynamic array, and there is no index. + if (search_results.net && search_results.net->data_type()==IVL_VT_DARRAY + && search_results.path_item.index.size()==0) { + + // Get the method name that we are looking for. + perm_string method_name = search_results.path_tail.back().name; + + if (method_name == "size") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: size() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$size", IVL_VT_BOOL, 32, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + + cerr << get_fileline() << ": error: Method " << method_name + << " is not a dynamic array method." << endl; + return 0; + } + + // Queue methods. This handles the case that the located signal is a + // QUEUE object, and there is a method. + if (search_results.net && search_results.net->data_type()==IVL_VT_QUEUE + && search_results.path_item.index.size()==0) { + + // Get the method name that we are looking for. + perm_string method_name = search_results.path_tail.back().name; + + if (method_name == "size") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: size() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$size", IVL_VT_BOOL, 32, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + + if (method_name == "pop_back") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: pop_back() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_back", + expr_type_, expr_width_, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + + if (method_name == "pop_front") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: pop_front() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_front", + expr_type_, expr_width_, 1); + sys_expr->set_line(*this); + sys_expr->parm(0, sub_expr); + return sys_expr; + } + + cerr << get_fileline() << ": error: Method " << method_name + << " is not a queue method." << endl; + des->errors += 1; + return 0; + } + + // Enumeration methods. + if (search_results.net && search_results.net->enumeration()) { + + NetNet*net = search_results.net; + const netenum_t*netenum = net->enumeration(); + + // Get the method name that we are looking for. + perm_string method_name = search_results.path_tail.back().name; + + PExpr*tmp = parms_.size() ? parms_[0] : 0; + return check_for_enum_methods(this, des, scope, + netenum, path_, + method_name, sub_expr, + expr_wid, tmp, + parms_.size()); + } + + // Class methods. Generate function call to the class method. + if (sub_expr->expr_type()==IVL_VT_CLASS) { + + // Get the method name that we are looking for. + perm_string method_name = search_results.path_tail.back().name; + + NetNet*net = search_results.net; + const netclass_t*class_type = net->class_type(); + ivl_assert(*this, class_type); + NetScope*method = class_type->method_from_name(method_name); + + if (method == 0) { + cerr << get_fileline() << ": Error: " << method_name + << " is not a method of class " << class_type->get_name() + << "." << endl; + des->errors += 1; + return 0; + } + + NetFuncDef*def = method->func_def(); + ivl_assert(*this, def); + + NetNet*res = method->find_signal(method->basename()); + ivl_assert(*this, res); + + vectorparms; + + NetESignal*ethis = new NetESignal(net); + ethis->set_line(*this); + parms.push_back(ethis); + + parms.resize(1 + parms_.size()); + elaborate_arguments_(des, scope, def, false, parms, 1); + + NetESignal*eres = new NetESignal(res); + NetEUFunc*call = new NetEUFunc(scope, method, eres, parms, false); + call->set_line(*this); + return call; + } + + // String methods. + if (sub_expr->expr_type()==IVL_VT_STRING) { + + // Get the method name that we are looking for. + perm_string method_name = search_results.path_tail.back().name; if (method_name == "len") { NetESFunc*sys_expr = new NetESFunc("$ivl_string_method$len", IVL_VT_BOOL, 32, 1); - sys_expr->parm(0, new NetESignal(net)); + sys_expr->parm(0, sub_expr); return sys_expr; } if (method_name == "atoi") { NetESFunc*sys_expr = new NetESFunc("$ivl_string_method$atoi", IVL_VT_BOOL, integer_width, 1); - sys_expr->parm(0, new NetESignal(net)); + sys_expr->parm(0, sub_expr); return sys_expr; } if (method_name == "atoreal") { NetESFunc*sys_expr = new NetESFunc("$ivl_string_method$atoreal", IVL_VT_REAL, 1, 1); - sys_expr->parm(0, new NetESignal(net)); + sys_expr->parm(0, sub_expr); return sys_expr; } if (method_name == "atohex") { NetESFunc*sys_expr = new NetESFunc("$ivl_string_method$atohex", IVL_VT_BOOL, integer_width, 1); - sys_expr->parm(0, new NetESignal(net)); + sys_expr->parm(0, sub_expr); return sys_expr; } @@ -2798,7 +3197,7 @@ NetExpr* PECallFunction::elaborate_expr_method_net_(Design*des, NetScope*scope, sys_expr->set_line(*this); // First argument is the source string. - sys_expr->parm(0, new NetESignal(net)); + sys_expr->parm(0, sub_expr); ivl_assert(*this, parms_.size() == 2); NetExpr*tmp; @@ -2813,133 +3212,43 @@ NetExpr* PECallFunction::elaborate_expr_method_net_(Design*des, NetScope*scope, return sys_expr; } - } - if (const netenum_t*netenum = net->enumeration()) { - // We may need the net expression for the - // enumeration variable so get it. - NetESignal*expr = new NetESignal(net); - expr->set_line(*this); - // This expression cannot be a select! - assert(use_path.back().index.empty()); - - PExpr*tmp = parms_.size() ? parms_[0] : 0; - return check_for_enum_methods(this, des, scope, - netenum, use_path, - method_name, expr, - expr_wid, tmp, - parms_.size()); - } - - if (net->darray_type()) { - - if (method_name == "size") { - if (parms_.size() != 0) { - cerr << get_fileline() << ": error: size() method " - << "takes no arguments" << endl; - des->errors += 1; - } - NetESFunc*sys_expr = new NetESFunc("$size", - IVL_VT_BOOL, 32, 1); - sys_expr->set_line(*this); - - NetESignal*arg = new NetESignal(net); - arg->set_line(*net); - - sys_expr->parm(0, arg); - return sys_expr; - } - } - - if (net->queue_type()) { - if (method_name == "pop_back") { - if (parms_.size() != 0) { - cerr << get_fileline() << ": error: pop_back() method " - << "takes no arguments" << endl; - des->errors += 1; - } - NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_back", - expr_type_, - expr_width_, 1); - sys_expr->set_line(*this); - - NetESignal*arg = new NetESignal(net); - arg->set_line(*net); - - sys_expr->parm(0, arg); - return sys_expr; - } - - if (method_name == "pop_front") { - if (parms_.size() != 0) { - cerr << get_fileline() << ": error: pop_front() method " - << "takes no arguments" << endl; - des->errors += 1; - } - NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_front", - expr_type_, - expr_width_, 1); - sys_expr->set_line(*this); - - NetESignal*arg = new NetESignal(net); - arg->set_line(*net); - - sys_expr->parm(0, arg); - return sys_expr; - } - - } - - if (const netclass_t*class_type = net->class_type()) { - NetScope*func = class_type->method_from_name(method_name); - if (func == 0) { - return 0; - } - - NetFuncDef*def = func->func_def(); - ivl_assert(*this, def); - - NetNet*res = func->find_signal(func->basename()); - ivl_assert(*this, res); - - vectorparms; - - NetESignal*ethis = new NetESignal(net); - ethis->set_line(*this); - parms.push_back(ethis); - - parms.resize(1 + parms_.size()); - elaborate_arguments_(des, scope, def, false, parms, 1); - - NetESignal*eres = new NetESignal(res); - NetEUFunc*call = new NetEUFunc(scope, func, eres, parms, false); - call->set_line(*this); - return call; + cerr << get_fileline() << ": error: Method " << method_name + << " is not a string method." << endl; + return 0; } return 0; } -NetExpr* PECallFunction::elaborate_expr_method_par_(Design*, NetScope*scope, - const NetExpr*par, - ivl_type_t par_type, +/* + * Handle parameters differently because some must constant elimination is + * possible here. We know by definition that the par_val is a constant + * expression of some sort (it's a parameter value) and most methods are + * stable in the sense that they generate a constant value for a constant input. + */ +NetExpr* PECallFunction::elaborate_expr_method_par_(Design*des, NetScope*scope, + symbol_search_results&search_results, unsigned expr_wid) const { - pform_name_t use_path = path_; - perm_string method_name = peek_tail_name(use_path); - use_path.pop_back(); + ivl_assert(*this, search_results.par_val); + ivl_assert(*this, search_results.par_type); + + const NetExpr*par_val = search_results.par_val; + ivl_type_t par_type = search_results.par_type; + perm_string method_name = search_results.path_tail.back().name; // If the parameter is of type string, then look for the standard string - // method. Return an error if not found. Since we are assured that the + // methods. Return an error if not found. Since we are assured that the // expression is a constant string, it should be able to calculate the // result at compile time. if (dynamic_cast(par_type)) { - const NetECString*par_string = dynamic_cast(par); - ivl_assert(*par, par_string); + const NetECString*par_string = dynamic_cast(par_val); + ivl_assert(*par_val, par_string); string par_value = par_string->value().as_string(); - if (method_name == "len") { + if (method_name=="len") { NetEConst*use_val = make_const_val(par_value.size()); use_val->set_line(*this); return pad_to_width(use_val, expr_wid, *this); @@ -2978,9 +3287,7 @@ NetExpr* PECallFunction::elaborate_expr_method_par_(Design*, NetScope*scope, cerr << get_fileline() << ": : " << *par_type << endl; cerr << get_fileline() << ": : in scope " << scope_path(scope) << endl; - // Returning 0 here will cause the caller to print an error message and - // increment the error count, so there is no need to increment - // des->error_count here. + des->errors += 1; return 0; } @@ -3972,6 +4279,11 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return 0; } + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "Found net " << net->name() << " for expr " << *this << endl; + } + if (const netdarray_t*array_type = dynamic_cast (ntype)) { if (array_type->type_compatible(net->net_type())) { NetESignal*tmp = new NetESignal(net); @@ -6250,23 +6562,22 @@ unsigned PENumber::test_width(Design*, NetScope*, width_mode_t&mode) return expr_width_; } -NetExpr* PENumber::elaborate_expr(Design*des, NetScope*, ivl_type_t ntype, unsigned) const +NetExpr* PENumber::elaborate_expr(Design*, NetScope*, ivl_type_t ntype, unsigned) const { - // Icarus allows dynamic arrays to be initialised with a single value. + if (debug_elaborate) { + cerr << get_fileline() << ": PENumber::elaborate_expr: " + << "expression: " << *this << endl; + if (ntype) + cerr << get_fileline() << ": PENumber::elaborate_expr: " + << "ntype=" << *ntype << endl; + } + + // Icarus allows dynamic arrays to be initialised with a single value. if (const netdarray_t*array_type = dynamic_cast (ntype)) ntype = array_type->element_type(); - const netvector_t*use_type = dynamic_cast (ntype); - if (use_type == 0) { - cerr << get_fileline() << ": internal error: " - << "I don't know how cast numbers to this type." - << endl; - des->errors += 1; - return 0; - } - - // Special case: If the context type is REAL, then cast the - // vector value to a real and return a NetECReal. + // Special case: If the context type is REAL, then cast the + // vector value to a real and return a NetECReal. if (ntype->base_type() == IVL_VT_REAL) { verireal val (value_->as_long()); NetECReal*tmp = new NetECReal(val); @@ -6275,8 +6586,8 @@ NetExpr* PENumber::elaborate_expr(Design*des, NetScope*, ivl_type_t ntype, unsig } verinum use_val = value(); - use_val .has_sign( use_type->get_signed() ); - use_val = cast_to_width(use_val, use_type->packed_width()); + use_val.has_sign( ntype->get_signed() ); + use_val = cast_to_width(use_val, ntype->packed_width()); NetEConst*tmp = new NetEConst(use_val); tmp->set_line(*this); diff --git a/elab_sig.cc b/elab_sig.cc index a86b34e78..c027ce033 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -913,6 +913,7 @@ bool test_ranges_eeq(const vector&lef, const vector&rig) return true; } + /* * Elaborate a source wire. The "wire" is the declaration of wires, * registers, ports and memories. The parser has already merged the @@ -950,6 +951,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const << ", wtype=" << wtype << ", data_type_=" << data_type_ << ", is_implicit_scalar=" << (is_implicit_scalar?"true":"false") + << ", unpacked_.size()=" << unpacked_.size() << endl; } @@ -1086,8 +1088,8 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const // dimensions, then turn this into a dynamic array and // put all the packed dimensions there. if (use_lidx==0 && use_ridx==0) { - netvector_t*vec = new netvector_t(packed_dimensions, data_type_); - vec->set_signed(get_signed()); + ivl_type_t vec = make_ivl_type(data_type_, packed_dimensions, + get_signed()); packed_dimensions.clear(); ivl_assert(*this, netdarray==0); netdarray = new netdarray_t(vec); @@ -1097,8 +1099,8 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const // Special case: Detect the mark for a QUEUE // declaration, which is the dimensions [null:max_idx]. if (dynamic_cast(use_lidx)) { - netvector_t*vec = new netvector_t(packed_dimensions, data_type_); - vec->set_signed(get_signed()); + ivl_type_t vec = make_ivl_type(data_type_, packed_dimensions, + get_signed()); packed_dimensions.clear(); ivl_assert(*this, netdarray==0); long max_idx; @@ -1232,7 +1234,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const const netenum_t*use_enum = base_type_scope->find_enumeration_for_name(des, sample_name->name); if (debug_elaborate) { - cerr << get_fileline() << ": " << __func__ << ": " + cerr << get_fileline() << ": PWire::elaborate_sig: " << "Create signal " << wtype << " enumeration " << name_ << " in scope " << scope_path(scope) @@ -1247,9 +1249,10 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const } else if (netdarray) { if (debug_elaborate) { - cerr << get_fileline() << ": " << __func__ << ": " - << "Create signal " << wtype - << " dynamic array " << name_ + cerr << get_fileline() << ": PWire::elaborate_sig: " + << "Create signal wtype=" << wtype + << " name=" << name_ + << " netdarray=" << *netdarray << " in scope " << scope_path(scope) << endl; } @@ -1271,6 +1274,21 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const sig = new NetNet(scope, name_, wtype, unpacked_dimensions, &netstring_t::type_string); + } else if (set_data_type_==0 && data_type_==IVL_VT_STRING) { + + // Signal declared as: string foo; + if (debug_elaborate) { + cerr << get_fileline() << ": PWire::elaborate_sig: " + << "Create signal " << wtype + << " string " + << name_ << " in scope " << scope_path(scope) + << " without set_data_type_" + << endl; + } + + sig = new NetNet(scope, name_, wtype, unpacked_dimensions, + &netstring_t::type_string); + } else if (parray_type_t*parray_type = dynamic_cast(set_data_type_)) { // The pform gives us a parray_type_t for packed arrays // that show up in type definitions. This can be handled @@ -1295,8 +1313,8 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const } else { if (debug_elaborate) { - cerr << get_fileline() << ": " << __func__ << ": " - << "Create signal " << wtype + cerr << get_fileline() << ": PWire::elaborate_sig: " + << "Create vector signal " << wtype << " data_type=" << data_type_; if (!get_scalar()) { cerr << " " << packed_dimensions; @@ -1309,7 +1327,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (use_data_type == IVL_VT_NO_TYPE) { use_data_type = IVL_VT_LOGIC; if (debug_elaborate) { - cerr << get_fileline() << ": " << __func__ << ": " + cerr << get_fileline() << ": PWire::elaborate_sig: " << "Signal " << name_ << " in scope " << scope_path(scope) << " defaults to data type " << use_data_type << endl; diff --git a/elaborate.cc b/elaborate.cc index c8c769067..1d86f941b 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -3371,7 +3371,8 @@ NetProc* PCondit::elaborate(Design*des, NetScope*scope) const assert(scope); if (debug_elaborate) - cerr << get_fileline() << ": debug: Elaborate condition statement" + cerr << get_fileline() << ": PCondit::elaborate: " + << "Elaborate condition statement" << " with conditional: " << *expr_ << endl; // Elaborate and try to evaluate the conditional expression. diff --git a/make_ivl_type.cc b/make_ivl_type.cc new file mode 100644 index 000000000..921d63487 --- /dev/null +++ b/make_ivl_type.cc @@ -0,0 +1,48 @@ + +/* + * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "nettypes.h" +# include "netscalar.h" +# include "netvector.h" + +ivl_type_t make_ivl_type(ivl_variable_type_t vt, + const std::vector&packed_dimensions, + bool signed_flag, bool isint_flag) +{ + netvector_t*vec; + + if (packed_dimensions.size() > 0) { + vec = new netvector_t(packed_dimensions, vt); + vec->set_signed(signed_flag); + return vec; + } + + switch (vt) { + case IVL_VT_REAL: + return &netreal_t::type_real; + case IVL_VT_STRING: + return &netstring_t::type_string; + default: + vec = new netvector_t(packed_dimensions, vt); + vec->set_signed(signed_flag); + vec->set_isint(isint_flag); + return vec; + } +} diff --git a/netmisc.h b/netmisc.h index 900b5480c..97a46ef50 100644 --- a/netmisc.h +++ b/netmisc.h @@ -23,6 +23,93 @@ class netsarray_t; +/* + * Search for a hierarchical name. The input path is one or more name + * components (name_component_t) which describe a path to the object. The + * simplest case is the path is a single name_component_t. This is the most + * usual case. More complex cases might include a string of name components + * that end in an item or scope, like this: + * + * a.b[1].c + * + * In this case, the "path input would include a.b.c, with index expressions + * on name_component_t for "b". In this case, usually "c" is the found item + * and "a" and "b" are scopes that lead up to the item. + * + * The search will stop when it finds a component in the path that is an + * object of some sort (other then a scope. So for example, if a.b is an + * array, then the search for a.b[1].c will stop at a.b, leave b[1] in + * path_item, and "c" in path_tail. It is up to the caller to then note that + * "c" must be a method of some sort. + */ +struct symbol_search_results { + inline symbol_search_results() { + scope = 0; + net = 0; + par_val = 0; + par_type = 0; + eve = 0; + } + + inline bool is_scope() const { + if (net) return false; + if (eve) return false; + if (par_val) return false; + if (scope) return true; + return false; + } + + inline bool is_found() const { + if (net) return true; + if (eve) return true; + if (par_val) return true; + if (scope) return true; + return false; + } + + // Scope where symbol was located. This is set in all cases, + // assuming the search succeeded. + NetScope*scope; + // If this was a net, the signal itself. + NetNet*net; + // If this was a parameter, the value expression and the + // optional value dimensions. + const NetExpr*par_val; + ivl_type_t par_type; + // If this is a named event, ... + NetEvent*eve; + + // Store bread crumbs of the search here. The path_tail is the parts of + // the original path that were not found, or are after an object (and so + // are probably members or methods.) + pform_name_t path_tail; + // The path_item is the final name (possibly before the path_tail items) + // that identifies the object. This name may contain index + // expressions. Parts of the path left of the path_item name scopes, and + // should have all been resolved into the "scope" member above. If the + // search result is a scope, then this path_item is also the name of the + // scope identified. + name_component_t path_item; +}; + +/* + * Test the search results and return true if this represents a function + * return value. That will be the case if the object is a net, the scope + * containing the object is a FUNCtion, and the containing scope and the + * object have the same name. + */ +static inline bool test_function_return_value(const symbol_search_results&search_results) +{ + if (!search_results.net) return false; + if (search_results.scope->type()!=NetScope::FUNC) return false; + if (search_results.net->name() != search_results.scope->basename()) return false; + return true; +} + +extern bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, + pform_name_t path, struct symbol_search_results*res, + NetScope*start_scope = 0); + /* * Search for a symbol using the "start" scope as the starting * point. If the path includes a scope part, then locate the diff --git a/nettypes.h b/nettypes.h index b2254026a..f5b9f7287 100644 --- a/nettypes.h +++ b/nettypes.h @@ -59,6 +59,13 @@ class ivl_type_s { virtual bool test_compatibility(ivl_type_t that) const; }; +/* + * Convenience functions for making ivl_type_t objects from various inputs. + */ +extern ivl_type_t make_ivl_type(ivl_variable_type_t vt, + const std::vector&packed_dimensions, + bool signed_flag =false, bool isint_flag =false); + /* * There are a couple types of array types. This class represents the * common bits of array types. diff --git a/pform_types.h b/pform_types.h index 652d7df04..6a3caaca4 100644 --- a/pform_types.h +++ b/pform_types.h @@ -111,9 +111,13 @@ struct index_component_t { }; struct name_component_t { - explicit name_component_t(perm_string n) : name(n) { } + inline name_component_t() { } + inline explicit name_component_t(perm_string n) : name(n) { } ~name_component_t() { } + // Return true if this component is nil. + inline bool empty() const { return name.nil(); } + perm_string name; std::listindex; }; diff --git a/symbol_search.cc b/symbol_search.cc index acb59abdc..3185c6e04 100644 --- a/symbol_search.cc +++ b/symbol_search.cc @@ -20,92 +20,112 @@ # include "netlist.h" # include "netmisc.h" +# include "compiler.h" # include "ivl_assert.h" /* - * Search for the hierarchical name. + * Search for the hierarchical name. The path may have multiple components. If + * that's the case, then recursively pull the path apart until we find the + * first item in the path, look that up, and work our way up. In most cases, + * the path will be a string of scopes, with an object at the end. But if we + * find an object before the end, then the tail will have to be figured out by + * the initial caller. */ -struct symbol_search_results { - inline symbol_search_results() { - scope = 0; - net = 0; - par_val = 0; - par_type = 0; - eve = 0; - } - inline bool is_scope() const { - if (net) return false; - if (eve) return false; - if (par_val) return false; - if (scope) return true; - return false; - } - - // Scope where symbol was located. This is set in all cases, - // assuming the search succeeded. - NetScope*scope; - // If this was a net, the signal itself. - NetNet*net; - // If this was a parameter, the value expression and the - // optional value dimensions. - const NetExpr*par_val; - ivl_type_t par_type; - // If this is a named event, ... - NetEvent*eve; -}; - -static bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, - pform_name_t path, struct symbol_search_results*res, - NetScope*start_scope = 0) +bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, + pform_name_t path, struct symbol_search_results*res, + NetScope*start_scope) { assert(scope); bool prefix_scope = false; - bool recurse_flag = false; + + if (debug_elaborate) { + cerr << li->get_fileline() << ": symbol_search: " + << "scope: " << scope_path(scope) << endl; + cerr << li->get_fileline() << ": symbol_search: " + << "path: " << path << endl; + if (start_scope) + cerr << li->get_fileline() << ": symbol_search: " + << "start_scope: " << scope_path(start_scope) << endl; + } assert(li); ivl_assert(*li, ! path.empty()); name_component_t path_tail = path.back(); path.pop_back(); - // If this is a recursive call, then we need to know that so - // that we can enable the search for scopes. Set the - // recurse_flag to true if this is a recurse. + // If this is a recursive call, then we need to know that so + // that we can enable the search for scopes. Set the + // recurse_flag to true if this is a recurse. if (start_scope==0) start_scope = scope; - else - recurse_flag = true; - // If there are components ahead of the tail, symbol_search - // recursively. Ideally, the result is a scope that we search - // for the tail key, but there are other special cases as well. + // If there are components ahead of the tail, symbol_search + // recursively. Ideally, the result is a scope that we search + // for the tail key, but there are other special cases as well. if (! path.empty()) { - symbol_search_results recurse; - bool flag = symbol_search(li, des, scope, path, &recurse, start_scope); + bool flag = symbol_search(li, des, scope, path, res, start_scope); if (! flag) return false; - // The prefix is found to be a scope, so switch to that - // scope, set the hier_path to turn off upwards searches, - // and continue our search for the tail. - if (recurse.is_scope()) { - scope = recurse.scope; + // The prefix is found to be something besides a scope. Put the + // tail into the path_tail of the result, and return success. The + // caller needs to deal with that tail bit. Note that the + // path_tail is a single item, but we might have been called + // recursively, so the complete tail will be built up as we unwind. + if (res->is_found() && !res->is_scope()) { + if (!path_tail.empty()) + res->path_tail.push_back(path_tail); + return true; + } + + // The prefix is found to be a scope, so switch to that + // scope, set the hier_path to turn off upwards searches, + // and continue our search for the tail. + if (res->is_scope()) { + scope = res->scope; prefix_scope = true; + if (debug_scopes || debug_elaborate) { + cerr << li->get_fileline() << ": symbol_search: " + << "Prefix scope " << scope_path(scope) << endl; + } + if (scope->is_auto()) { cerr << li->get_fileline() << ": error: Hierarchical " "reference to automatically allocated item " "`" << path_tail.name << "' in path `" << path << "'" << endl; des->errors += 1; } + } else { - // Prefix is present, but is NOT a scope. Fail! + // Prefix is present, but is NOT a scope. Fail! Actually, this + // should not happen, since this is the "not found" case, and we + // should have returned already. + assert(0); return false; } } + bool passed_module_boundary = false; + + // At this point, we've stripped right-most components until the search + // found the scope part of the path, or there is no scope part of the + // path. For example, if the path in was s1.s2.x, we found the scope + // s1.s2, res->is_scope() is true, and path_tail is x. We look for x + // now. The preceeding code set prefix_scope=true to ease our test below. + // + // If the input was x (without prefixes) then we don't know if x is a + // scope or item. In this case, res->is_found() is false and we may need + // to scan upwards to find the scope or item. while (scope) { + if (debug_scopes || debug_elaborate) { + cerr << li->get_fileline() << ": symbol_search: " + << "Looking for " << path_tail + << " in scope " << scope_path(scope) + << " prefix_scope=" << prefix_scope << endl; + } if (scope->genvar_tmp.str() && path_tail.name == scope->genvar_tmp) return false; @@ -115,22 +135,39 @@ static bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, return false; } - if (NetNet*net = scope->find_signal(path_tail.name)) { - res->scope = scope; - res->net = net; - return true; - } + // These items cannot be seen outside the bounding module where + // the search starts. But we continue searching up because scope + // names can match. For example: + // + // module top; + // int not_ok; + // dut foo(...); + // endmodule + // module dut; + // ... not_ok; // <-- Should NOT match. + // ... top.not_ok; // Matches. + // endmodule + if (!passed_module_boundary) { + if (NetNet*net = scope->find_signal(path_tail.name)) { + res->scope = scope; + res->net = net; + res->path_item = path_tail; + return true; + } - if (NetEvent*eve = scope->find_event(path_tail.name)) { - res->scope = scope; - res->eve = eve; - return true; - } + if (NetEvent*eve = scope->find_event(path_tail.name)) { + res->scope = scope; + res->eve = eve; + res->path_item = path_tail; + return true; + } - if (const NetExpr*par = scope->get_parameter(des, path_tail.name, res->par_type)) { - res->scope = scope; - res->par_val = par; - return true; + if (const NetExpr*par = scope->get_parameter(des, path_tail.name, res->par_type)) { + res->scope = scope; + res->par_val = par; + res->path_item = path_tail; + return true; + } } if (NetScope*import_scope = scope->find_import(des, path_tail.name)) { @@ -138,42 +175,91 @@ static bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, continue; } - if (recurse_flag) { + // Could not find an object. Maybe this is a child scope name? If + // so, evaluate the path conponents to find the exact scope this + // refers to. This item might be: + // .s + // .s[n] + // etc. The scope->child_byname tests if the name exists, and if + // it does, the eval_path_component() evaluates any [n] + // expressions to constants to generate an hname_t object for a + // more complete scope name search. Note that the index + // expressions for scope names must be constant. + if (scope->child_byname(path_tail.name)) { bool flag = false; hname_t path_item = eval_path_component(des, start_scope, path_tail, flag); if (flag) { cerr << li->get_fileline() << ": XXXXX: Errors evaluating scope index" << endl; - } else if (NetScope*chld = des->find_scope(scope, path_item)) { + } else if (NetScope*chld = scope->child(path_item)) { res->scope = chld; + res->path_item = path_tail; return true; } } - // Don't scan up if we are searching within a prefixed scope. + // Don't scan up if we are searching within a prefixed scope. if (prefix_scope) break; - // Don't scan up past a module boundary. - if (scope->type()==NetScope::MODULE && !scope->nested_module()) - scope = 0; - else - scope = scope->parent(); + // Special case: We can match the module name of a parent + // module. That means if the current scope is a module of type + // "mod", then "mod" matches the current scope. This is fairly + // obscure, but looks like this: + // + // module foo; + // reg x; + // ... foo.x; // This matches x in myself. + // endmodule + // + // This feature recurses, so code in subscopes of foo can refer to + // foo by the name "foo" as well. In general, anything within + // "foo" can use the name "foo" to reference it. + if (scope->type()==NetScope::MODULE && scope->module_name()==path_tail.name) { + res->scope = scope; + res->path_item = path_tail; + return true; + } - // Last chance - try the compilation unit. + // If there is no prefix, then we are free to scan upwards looking + // for a scope name. Note that only scopes can be searched for up + // past module boundaries. To handle that, set a flag to indicate + // that we passed a module boundary on the way up. + if (scope->type()==NetScope::MODULE && !scope->nested_module()) + passed_module_boundary = true; + + scope = scope->parent(); + + // Last chance - try the compilation unit. Note that modules may + // reference nets/variables in the compilation unit, even if they + // cannot reference variables in containing scope. + // + // int ok = 1; + // module top; + // int not_ok = 2; + // dut foo(); + // endmodule + // + // module dut; + // ... = ok; // This reference is OK + // ... = not_ok; // This reference is NOT OK. + // endmodule if (scope == 0 && start_scope != 0) { scope = start_scope->unit(); start_scope = 0; + passed_module_boundary = false; } } - // Last chance: this is a single name, so it might be the name - // of a root scope. Ask the design if this is a root - // scope. This is only possible if there is no prefix. + + // Last chance: this is a single name, so it might be the name + // of a root scope. Ask the design if this is a root + // scope. This is only possible if there is no prefix. if (prefix_scope==false) { hname_t path_item (path_tail.name); scope = des->find_scope(path_item); if (scope) { res->scope = scope; + res->path_item = path_tail; return true; } } @@ -193,6 +279,23 @@ NetScope*symbol_search(const LineInfo*li, Design*des, NetScope*scope, { symbol_search_results recurse; bool flag = symbol_search(li, des, scope, path, &recurse); + + net = 0; + par = 0; + par_type = 0; + eve = 0; + + // The compatible version doesn't know how to handle unmatched tail + // components, so report them as errors. + if (! recurse.path_tail.empty()) { + if (debug_elaborate) { + cerr << li->get_fileline() << ": symbol_search (compat): " + << "path_tail items found: " << recurse.path_tail << endl; + } + return 0; + } + + // Convert the extended results to the compatible results. net = recurse.net; par = recurse.par_val; par_type = recurse.par_type; @@ -201,8 +304,5 @@ NetScope*symbol_search(const LineInfo*li, Design*des, NetScope*scope, return 0; } - if (recurse.is_scope()) - return recurse.scope; - return recurse.scope; }