/* * Copyright (c) 2000-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / 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 "config.h" # include # include # include # include "Module.h" # include "PClass.h" # include "PExpr.h" # include "PGate.h" # include "PGenerate.h" # include "PPackage.h" # include "PTask.h" # include "PWire.h" # include "Statement.h" # include "compiler.h" # include "netlist.h" # include "netmisc.h" # include "netclass.h" # include "netenum.h" # include "netvector.h" # include "netdarray.h" # include "netparray.h" # include "netqueue.h" # include "netscalar.h" # include "util.h" # include "ivl_assert.h" using namespace std; #if 0 /* These functions are not currently used. */ static bool get_const_argument(NetExpr*exp, verinum&res) { switch (exp->expr_type()) { case IVL_VT_REAL: { NetECReal*cv = dynamic_cast(exp); if (cv == 0) return false; verireal tmp = cv->value(); res = verinum(tmp.as_long()); break; } case IVL_VT_BOOL: case IVL_VT_LOGIC: { NetEConst*cv = dynamic_cast(exp); if (cv == 0) return false; res = cv->value(); break; } default: assert(0);; } return true; } static bool get_const_argument(NetExpr*exp, long&res) { verinum tmp; bool rc = get_const_argument(exp, tmp); if (rc == false) return false; res = tmp.as_long(); return true; } #endif void Statement::elaborate_sig(Design*, NetScope*) const { } bool PScope::elaborate_sig_wires_(Design*des, NetScope*scope) const { bool flag = true; for (map::const_iterator wt = wires.begin() ; wt != wires.end() ; ++ wt ) { PWire*cur = (*wt).second; NetNet*sig = cur->elaborate_sig(des, scope); if (sig && (sig->scope() == scope) && (sig->port_type() == NetNet::PREF)) { cerr << cur->get_fileline() << ": sorry: " << "Reference ports not supported yet." << endl; des->errors += 1; } /* If the signal is an input and is also declared as a reg, then report an error. */ if (sig && (sig->scope() == scope) && (scope->type() == NetScope::MODULE) && (sig->port_type() == NetNet::PINPUT) && (sig->type() == NetNet::REG)) { cerr << cur->get_fileline() << ": error: Port " << cur->basename() << " of module " << scope->module_name() << " is declared as input and as a reg type." << endl; des->errors += 1; } if (sig && (sig->scope() == scope) && (scope->type() == NetScope::MODULE) && (sig->port_type() == NetNet::PINOUT) && (sig->type() == NetNet::REG)) { cerr << cur->get_fileline() << ": error: Port " << cur->basename() << " of module " << scope->module_name() << " is declared as inout and as a reg type." << endl; des->errors += 1; } if (sig && (sig->scope() == scope) && (scope->type() == NetScope::MODULE) && (sig->port_type() == NetNet::PINOUT) && (sig->data_type() == IVL_VT_REAL)) { cerr << cur->get_fileline() << ": error: Port " << cur->basename() << " of module " << scope->module_name() << " is declared as a real inout port." << endl; des->errors += 1; } } return flag; } static void elaborate_sig_funcs(Design*des, NetScope*scope, const map&funcs) { typedef map::const_iterator mfunc_it_t; for (mfunc_it_t cur = funcs.begin() ; cur != funcs.end() ; ++ cur ) { hname_t use_name ( (*cur).first ); NetScope*fscope = scope->child(use_name); if (fscope == 0) { cerr << (*cur).second->get_fileline() << ": internal error: " << "Child scope for function " << (*cur).first << " missing in " << scope_path(scope) << "." << endl; des->errors += 1; continue; } if (debug_elaborate) { cerr << cur->second->get_fileline() << ": elaborate_sig_funcs: " << "Elaborate function " << use_name << " in " << scope_path(fscope) << endl; } cur->second->elaborate_sig(des, fscope); } } static void elaborate_sig_tasks(Design*des, NetScope*scope, const map&tasks) { typedef map::const_iterator mtask_it_t; for (mtask_it_t cur = tasks.begin() ; cur != tasks.end() ; ++ cur ) { NetScope*tscope = scope->child( hname_t((*cur).first) ); assert(tscope); (*cur).second->elaborate_sig(des, tscope); } } static void elaborate_sig_classes(Design*des, NetScope*scope, const map&classes) { for (map::const_iterator cur = classes.begin() ; cur != classes.end() ; ++ cur) { netclass_t*use_class = scope->find_class(des, cur->second->pscope_name()); use_class->elaborate_sig(des, cur->second); } } bool PPackage::elaborate_sig(Design*des, NetScope*scope) const { bool flag = true; if (debug_elaborate) { cerr << get_fileline() << ": PPackage::elaborate_sig: " << "Start package scope=" << scope_path(scope) << endl; } flag = elaborate_sig_wires_(des, scope) && flag; // After all the wires are elaborated, we are free to // elaborate the ports of the tasks defined within this // module. Run through them now. elaborate_sig_funcs(des, scope, funcs); elaborate_sig_tasks(des, scope, tasks); elaborate_sig_classes(des, scope, classes); if (debug_elaborate) { cerr << get_fileline() << ": PPackage::elaborate_sig: " << "Done package scope=" << scope_path(scope) << ", flag=" << flag << endl; } return flag; } bool Module::elaborate_sig(Design*des, NetScope*scope) const { bool flag = true; // Scan all the ports of the module, and make sure that each // is connected to wires that have port declarations. for (unsigned idx = 0 ; idx < ports.size() ; idx += 1) { Module::port_t*pp = ports[idx]; if (pp == 0) continue; // The port has a name and an array of expressions. The // expression are all identifiers that should reference // wires within the scope. map::const_iterator wt; for (unsigned cc = 0 ; cc < pp->expr.size() ; cc += 1) { pform_name_t port_path (pp->expr[cc]->path()); // A concatenated wire of a port really should not // have any hierarchy. if (port_path.size() != 1) { cerr << get_fileline() << ": internal error: " << "Port " << port_path << " has a funny name?" << endl; des->errors += 1; } wt = wires.find(peek_tail_name(port_path)); if (wt == wires.end()) { cerr << get_fileline() << ": error: " << "Port " << port_path << " (" << (idx+1) << ") of module " << mod_name() << " is not declared within module." << endl; des->errors += 1; continue; } if ((*wt).second->get_port_type() == NetNet::NOT_A_PORT) { cerr << get_fileline() << ": error: " << "Port " << pp->expr[cc]->path() << " (" << (idx+1) << ") of module " << mod_name() << " has no direction declaration." << endl; des->errors += 1; } } } flag = elaborate_sig_wires_(des, scope) && flag; // Run through all the generate schemes to elaborate the // signals that they hold. Note that the generate schemes hold // the scopes that they instantiated, so we don't pass any // scope in. typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { (*cur) -> elaborate_sig(des, scope); } // Get all the gates of the module and elaborate them by // connecting them to the signals. The gate may be simple or // complex. What we are looking for is gates that are modules // that can create scopes and signals. const list&gl = get_gates(); for (list::const_iterator gt = gl.begin() ; gt != gl.end() ; ++ gt ) { flag &= (*gt)->elaborate_sig(des, scope); } // After all the wires are elaborated, we are free to // elaborate the ports of the tasks defined within this // module. Run through them now. elaborate_sig_funcs(des, scope, funcs); elaborate_sig_tasks(des, scope, tasks); elaborate_sig_classes(des, scope, classes); // initial and always blocks may contain begin-end and // fork-join blocks that can introduce scopes. Therefore, I // get to scan processes here. typedef list::const_iterator proc_it_t; for (proc_it_t cur = behaviors.begin() ; cur != behaviors.end() ; ++ cur ) { (*cur) -> statement() -> elaborate_sig(des, scope); } return flag; } void netclass_t::elaborate_sig(Design*des, PClass*pclass) { for (map::iterator cur = pclass->type->properties.begin() ; cur != pclass->type->properties.end() ; ++ cur) { if (! cur->second.qual.test_static()) continue; if (debug_elaborate) { cerr << pclass->get_fileline() << ": netclass_t::elaborate_sig: " << "Elaborate static property " << cur->first << " as signal in scope " << scope_path(class_scope_) << "." << endl; } list nil_list; ivl_type_t use_type = cur->second.type->elaborate_type(des, class_scope_); /* NetNet*sig = */ new NetNet(class_scope_, cur->first, NetNet::REG, nil_list, use_type); } for (map::iterator cur = pclass->funcs.begin() ; cur != pclass->funcs.end() ; ++ cur) { if (debug_elaborate) { cerr << cur->second->get_fileline() << ": netclass_t::elaborate_sig: " << "Elaborate signals in function method " << cur->first << endl; } NetScope*scope = class_scope_->child( hname_t(cur->first) ); ivl_assert(*cur->second, scope); cur->second->elaborate_sig(des, scope); } for (map::iterator cur = pclass->tasks.begin() ; cur != pclass->tasks.end() ; ++ cur) { if (debug_elaborate) { cerr << cur->second->get_fileline() << ": netclass_t::elaborate_sig: " << "Elaborate signals in task method " << cur->first << endl; } NetScope*scope = class_scope_->child( hname_t(cur->first) ); ivl_assert(*cur->second, scope); cur->second->elaborate_sig(des, scope); } } bool PGate::elaborate_sig(Design*, NetScope*) const { return true; } bool PGBuiltin::elaborate_sig(Design*, NetScope*) const { return true; } bool PGAssign::elaborate_sig(Design*, NetScope*) const { return true; } bool PGModule::elaborate_sig_mod_(Design*des, NetScope*scope, Module*rmod) const { bool flag = true; NetScope::scope_vec_t instance = scope->instance_arrays[get_name()]; for (unsigned idx = 0 ; idx < instance.size() ; idx += 1) { // I know a priori that the elaborate_scope created the scope // already, so just look it up as a child of the current scope. NetScope*my_scope = instance[idx]; assert(my_scope); if (my_scope->parent() != scope) { cerr << get_fileline() << ": internal error: " << "Instance " << scope_path(my_scope) << " is in parent " << scope_path(my_scope->parent()) << " instead of " << scope_path(scope) << endl; } assert(my_scope->parent() == scope); if (! rmod->elaborate_sig(des, my_scope)) flag = false; } return flag; } // Not currently used. #if 0 bool PGModule::elaborate_sig_udp_(Design*des, NetScope*scope, PUdp*udp) const { return true; } #endif bool PGenerate::elaborate_sig(Design*des, NetScope*container) const { if (direct_nested_) return elaborate_sig_direct_(des, container); bool flag = true; // Handle the special case that this is a CASE scheme. In this // case the PGenerate itself does not have the generated // item. Look instead for the case ITEM that has a scope // generated for it. if (scheme_type == PGenerate::GS_CASE) { if (debug_elaborate) cerr << get_fileline() << ": debug: generate case" << " elaborate_sig in scope " << scope_path(container) << "." << endl; typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { PGenerate*item = *cur; if (item->direct_nested_ || !item->scope_list_.empty()) { flag &= item->elaborate_sig(des, container); } } return flag; } typedef list::const_iterator scope_list_it_t; for (scope_list_it_t cur = scope_list_.begin() ; cur != scope_list_.end() ; ++ cur ) { NetScope*scope = *cur; if (scope->parent() != container) continue; if (debug_elaborate) cerr << get_fileline() << ": debug: Elaborate nets in " << "scope " << scope_path(*cur) << " in generate " << id_number << endl; flag = elaborate_sig_(des, *cur) & flag; } return flag; } bool PGenerate::elaborate_sig_direct_(Design*des, NetScope*container) const { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Direct nesting " << scope_name << " (scheme_type=" << scheme_type << ")" << " elaborate_sig in scope " << scope_path(container) << "." << endl; // Elaborate_sig for a direct nested generated scheme knows // that there are only sub_schemes to be elaborated. There // should be exactly 1 active generate scheme, search for it // using this loop. bool flag = true; typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { PGenerate*item = *cur; if (item->scheme_type == PGenerate::GS_CASE) { for (generate_it_t icur = item->generate_schemes.begin() ; icur != item->generate_schemes.end() ; ++ icur ) { PGenerate*case_item = *icur; if (case_item->direct_nested_ || !case_item->scope_list_.empty()) { flag &= case_item->elaborate_sig(des, container); } } } else { if (item->direct_nested_ || !item->scope_list_.empty()) { // Found the item, and it is direct nested. flag &= item->elaborate_sig(des, container); } } } return flag; } bool PGenerate::elaborate_sig_(Design*des, NetScope*scope) const { // Scan the declared PWires to elaborate the obvious signals // in the current scope. typedef map::const_iterator wires_it_t; for (wires_it_t wt = wires.begin() ; wt != wires.end() ; ++ wt ) { PWire*cur = (*wt).second; if (debug_elaborate) cerr << get_fileline() << ": debug: Elaborate PWire " << cur->basename() << " in scope " << scope_path(scope) << endl; cur->elaborate_sig(des, scope); } elaborate_sig_funcs(des, scope, funcs); elaborate_sig_tasks(des, scope, tasks); typedef list::const_iterator generate_it_t; for (generate_it_t cur = generate_schemes.begin() ; cur != generate_schemes.end() ; ++ cur ) { (*cur) -> elaborate_sig(des, scope); } typedef list::const_iterator pgate_list_it_t; for (pgate_list_it_t cur = gates.begin() ; cur != gates.end() ; ++ cur ) { (*cur) ->elaborate_sig(des, scope); } typedef list::const_iterator proc_it_t; for (proc_it_t cur = behaviors.begin() ; cur != behaviors.end() ; ++ cur ) { (*cur) -> statement() -> elaborate_sig(des, scope); } return true; } /* * A function definition exists within an elaborated module. This * matters when elaborating signals, as the ports of the function are * created as signals/variables for each instance of the * function. That is why PFunction has an elaborate_sig method. */ void PFunction::elaborate_sig(Design*des, NetScope*scope) const { if (scope->elab_stage() > 1) return; scope->set_elab_stage(2); perm_string fname = scope->basename(); assert(scope->type() == NetScope::FUNC); elaborate_sig_wires_(des, scope); NetNet*ret_sig; if (gn_system_verilog() && (fname=="new" || fname=="new@")) { // Special case: this is a constructor, so the return // signal is also the first argument. For example, the // source code for the definition may be: // function new(...); // endfunction // In this case, the "@" port (THIS_TOKEN) is the synthetic // "this" argument and we also use it as a return value at // the same time. ret_sig = scope->find_signal(perm_string::literal(THIS_TOKEN)); ivl_assert(*this, ret_sig); if (debug_elaborate) cerr << get_fileline() << ": PFunction::elaborate_sig: " << "Scope " << scope_path(scope) << " is a CONSTRUCTOR, so use \"this\" argument" << " as return value." << endl; } else { ivl_type_t ret_type; if (return_type_) { if (dynamic_cast (return_type_)) { ret_type = 0; } else { ret_type = return_type_->elaborate_type(des, scope->parent()); ivl_assert(*this, ret_type); } } else { netvector_t*tmp = new netvector_t(IVL_VT_LOGIC); tmp->set_scalar(true); ret_type = tmp; } if (ret_type) { if (debug_elaborate) { cerr << get_fileline() << ": PFunction::elaborate_sig: " << "return type: " << *ret_type << endl; if (return_type_) return_type_->pform_dump(cerr, 8); } list ret_unpacked; ret_sig = new NetNet(scope, fname, NetNet::REG, ret_unpacked, ret_type); ret_sig->set_line(*this); ret_sig->port_type(NetNet::POUTPUT); } else { ret_sig = 0; if (debug_elaborate) { cerr << get_fileline() << ": PFunction::elaborate_sig: " << "Detected that function is void." << endl; } } } vectorports; vectorpdef; elaborate_sig_ports_(des, scope, ports, pdef); NetFuncDef*def = new NetFuncDef(scope, ret_sig, ports, pdef); if (debug_elaborate) cerr << get_fileline() << ": PFunction::elaborate_sig: " << "Attach function definition " << scope_path(scope) << " with ret_sig width=" << (ret_sig? ret_sig->vector_width() : 0) << "." << endl; scope->set_func_def(def); // Look for further signals in the sub-statement if (statement_) statement_->elaborate_sig(des, scope); } /* * A task definition is a scope within an elaborated module. When we * are elaborating signals, the scopes have already been created, as * have the reg objects that are the parameters of this task. The * elaborate_sig method of PTask is therefore left to connect the * signals to the ports of the NetTaskDef definition. We know for * certain that signals exist (They are in my scope!) so the port * binding is sure to work. */ void PTask::elaborate_sig(Design*des, NetScope*scope) const { assert(scope->type() == NetScope::TASK); elaborate_sig_wires_(des, scope); vectorports; vectorpdefs; elaborate_sig_ports_(des, scope, ports, pdefs); NetTaskDef*def = new NetTaskDef(scope, ports, pdefs); scope->set_task_def(def); // Look for further signals in the sub-statement if (statement_) statement_->elaborate_sig(des, scope); } void PTaskFunc::elaborate_sig_ports_(Design*des, NetScope*scope, vector&ports, vector&pdefs) const { if (ports_ == 0) { ports.clear(); pdefs.clear(); /* Make sure the function has at least one input port. If it fails this test, print an error message. Keep going so we can find more errors. */ if (scope->type()==NetScope::FUNC && !gn_system_verilog()) { cerr << get_fileline() << ": error: " << "Function " << scope->basename() << " has no ports." << endl; cerr << get_fileline() << ": : " << "Functions must have at least one input port." << endl; des->errors += 1; } return; } ports.resize(ports_->size()); pdefs.resize(ports_->size()); for (size_t idx = 0 ; idx < ports_->size() ; idx += 1) { perm_string port_name = ports_->at(idx).port->basename(); ports[idx] = 0; pdefs[idx] = 0; NetNet*tmp = scope->find_signal(port_name); NetExpr*tmp_def = 0; if (tmp == 0) { cerr << get_fileline() << ": internal error: " << "task/function " << scope_path(scope) << " is missing port " << port_name << "." << endl; scope->dump(cerr); cerr << get_fileline() << ": Continuing..." << endl; des->errors += 1; continue; } // If the port has a default expression, elaborate // that expression here. if (ports_->at(idx).defe != 0) { if (tmp->port_type() == NetNet::PINPUT) { tmp_def = elab_and_eval(des, scope, ports_->at(idx).defe, -1, scope->need_const_func()); if (tmp_def == 0) { cerr << get_fileline() << ": error: Unable to evaluate " << *ports_->at(idx).defe << " as a port default expression." << endl; des->errors += 1; } } else { cerr << get_fileline() << ": sorry: Default arguments " "for subroutine output or inout ports are not " "yet supported." << endl; des->errors += 1; } } if (tmp->port_type() == NetNet::NOT_A_PORT) { cerr << get_fileline() << ": internal error: " << "task/function " << scope_path(scope) << " port " << port_name << " is a port but is not a port?" << endl; des->errors += 1; scope->dump(cerr); continue; } ports[idx] = tmp; pdefs[idx] = tmp_def; if (scope->type()==NetScope::FUNC && tmp->port_type()!=NetNet::PINPUT) { cerr << tmp->get_fileline() << ": error: " << "Function " << scope_path(scope) << " port " << port_name << " is not an input port." << endl; cerr << tmp->get_fileline() << ": : " << "Function arguments must be input ports." << endl; des->errors += 1; } if (tmp->unpacked_dimensions() != 0) { cerr << get_fileline() << ": sorry: Subroutine ports with " "unpacked dimensions are not yet supported." << endl; des->errors += 1; } } } void PBlock::elaborate_sig(Design*des, NetScope*scope) const { NetScope*my_scope = scope; if (pscope_name() != 0) { hname_t use_name (pscope_name()); my_scope = scope->child(use_name); if (my_scope == 0) { cerr << get_fileline() << ": internal error: " << "Unable to find child scope " << pscope_name() << " in this context?" << endl; des->errors += 1; my_scope = scope; } else { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "elaborate_sig descending into " << scope_path(my_scope) << "." << endl; elaborate_sig_wires_(des, my_scope); } } // elaborate_sig in the statements included in the // block. There may be named blocks in there. for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) list_[idx] -> elaborate_sig(des, my_scope); } void PCase::elaborate_sig(Design*des, NetScope*scope) const { if (items_ == 0) return; for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) { if ( (*items_)[idx]->stat ) (*items_)[idx]->stat ->elaborate_sig(des,scope); } } void PCondit::elaborate_sig(Design*des, NetScope*scope) const { if (if_) if_->elaborate_sig(des, scope); if (else_) else_->elaborate_sig(des, scope); } void PDelayStatement::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PDoWhile::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PEventStatement::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PForeach::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PForever::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PForStatement::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PRepeat::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } void PWhile::elaborate_sig(Design*des, NetScope*scope) const { if (statement_) statement_->elaborate_sig(des, scope); } static ivl_type_s*elaborate_type(Design*des, NetScope*scope, data_type_t*pform_type) { if (struct_type_t*struct_type = dynamic_cast(pform_type)) { ivl_type_s*use_type = struct_type->elaborate_type(des, scope); return use_type; } cerr << pform_type->get_fileline() << ": sorry: I don't know how to elaborate " << typeid(*pform_type).name() << " here." << endl; des->errors += 1; return 0; } static netparray_t* elaborate_parray_type(Design*des, NetScope*scope, const LineInfo*li, parray_type_t*data_type) { vectorpacked_dimensions; bool dimensions_ok = evaluate_ranges(des, scope, li, packed_dimensions, * data_type->dims); ivl_assert(*data_type, dimensions_ok); ivl_type_s*element_type = elaborate_type(des, scope, data_type->base_type); netparray_t*res = new netparray_t(packed_dimensions, element_type); //res->set_line(*data_type); return res; } bool test_ranges_eeq(const vector&lef, const vector&rig) { if (lef.size() != rig.size()) return false; vector::const_iterator lcur = lef.begin(); vector::const_iterator rcur = rig.begin(); while (lcur != lef.end()) { if (lcur->get_msb() != rcur->get_msb()) return false; if (lcur->get_lsb() != rcur->get_lsb()) return false; ++ lcur; ++ rcur; } return true; } /* * Elaborate a source wire. The "wire" is the declaration of wires, * registers, ports and memories. The parser has already merged the * multiple properties of a wire (i.e., "input wire"), so come the * elaboration this creates an object in the design that represents the * defined item. */ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const { // This sets the vector or array dimension size that will // cause a warning. For now, these warnings are permanently // enabled. const long warn_dimension_size = 1 << 30; NetNet::Type wtype = type_; bool is_implicit_scalar = false; if (wtype == NetNet::IMPLICIT) { wtype = NetNet::WIRE; is_implicit_scalar = true; } // Certain contexts, such as arguments to functions, presume // "reg" instead of "wire". The parser reports these as // IMPLICIT_REG. Also, certain cases, such as: // fun(string arg1) ... // are implicitly NOT scalar, so detect that case here. if (wtype == NetNet::IMPLICIT_REG) { wtype = NetNet::REG; if (data_type_!=IVL_VT_STRING) is_implicit_scalar = true; } if (debug_elaborate) { cerr << get_fileline() << ": PWire::elaborate_sig: " << "Signal " << basename() << ", wtype=" << wtype << ", data_type_=" << data_type_ << ", is_implicit_scalar=" << (is_implicit_scalar?"true":"false") << endl; } unsigned wid = 1; vectorpacked_dimensions; NetScope*base_type_scope = scope; if (set_data_type_ && !set_data_type_->name.nil()) base_type_scope = scope->find_typedef_scope(des, set_data_type_); des->errors += error_cnt_; if (port_set_ || net_set_) { if (warn_implicit_dimensions && port_set_ && net_set_ && net_.empty() && !port_.empty()) { cerr << get_fileline() << ": warning: " << "var/net declaration of " << basename() << " inherits dimensions from port declaration." << endl; } if (warn_implicit_dimensions && port_set_ && net_set_ && port_.empty() && !net_.empty()) { cerr << get_fileline() << ": warning: " << "Port declaration of " << basename() << " inherits dimensions from var/net." << endl; } bool dimensions_ok = true; vector plist, nlist; /* If they exist get the port definition MSB and LSB */ if (port_set_ && !port_.empty()) { if (debug_elaborate) { cerr << get_fileline() << ": PWire::elaborate_sig: " << "Evaluate ranges for port " << basename() << endl; } dimensions_ok &= evaluate_ranges(des, scope, this, plist, port_); nlist = plist; /* An implicit port can have a range so note that here. */ is_implicit_scalar = false; } assert(port_set_ || port_.empty()); /* If they exist get the net/etc. definition MSB and LSB */ if (net_set_ && !net_.empty() && dimensions_ok) { nlist.clear(); if (debug_elaborate) { cerr << get_fileline() << ": PWire::elaborate_sig: " << "Evaluate ranges for net " << basename() << endl; } dimensions_ok &= evaluate_ranges(des, base_type_scope, this, nlist, net_); } assert(net_set_ || net_.empty()); if (debug_elaborate) { cerr << get_fileline() << ": PWire::elaborate_sig: " << "Calculated ranges for " << basename() << ". Now check for consistency." << endl; } /* We have a port size error */ if (port_set_ && net_set_ && !test_ranges_eeq(plist, nlist)) { /* Scalar port with a vector net/etc. definition */ if (port_.empty()) { if (!gn_io_range_error_flag) { cerr << get_fileline() << ": warning: Scalar port ``" << name_ << "'' has a vectored net declaration " << nlist << "." << endl; } else { cerr << get_fileline() << ": error: Scalar port ``" << name_ << "'' has a vectored net declaration " << nlist << "." << endl; des->errors += 1; return 0; } } /* Vectored port with a scalar net/etc. definition */ if (net_.empty()) { cerr << port_.front().first->get_fileline() << ": error: Vectored port ``" << name_ << "'' " << plist << " has a scalar net declaration at " << get_fileline() << "." << endl; des->errors += 1; return 0; } /* Both vectored, but they have different ranges. */ if (!port_.empty() && !net_.empty()) { cerr << port_.front().first->get_fileline() << ": error: Vectored port ``" << name_ << "'' " << plist << " has a net declaration " << nlist << " at " << net_.front().first->get_fileline() << " that does not match." << endl; des->errors += 1; return 0; } } packed_dimensions = nlist; wid = netrange_width(packed_dimensions); if (wid > warn_dimension_size) { cerr << get_fileline() << ": warning: Vector size " "is greater than " << warn_dimension_size << "." << endl; } } unsigned nattrib = 0; attrib_list_t*attrib_list = evaluate_attributes(attributes, nattrib, des, scope); listunpacked_dimensions; netdarray_t*netdarray = 0; NetScope*array_type_scope = scope; if (uarray_type_ && !uarray_type_->name.nil()) array_type_scope = scope->find_typedef_scope(des, uarray_type_); for (list::const_iterator cur = unpacked_.begin() ; cur != unpacked_.end() ; ++cur) { PExpr*use_lidx = cur->first; PExpr*use_ridx = cur->second; // Special case: If we encounter an undefined // 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()); packed_dimensions.clear(); ivl_assert(*this, netdarray==0); netdarray = new netdarray_t(vec); continue; } // 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()); packed_dimensions.clear(); ivl_assert(*this, netdarray==0); long max_idx; if (use_ridx) { NetExpr*tmp = elab_and_eval(des, array_type_scope, use_ridx, -1, true); NetEConst*cv = dynamic_cast(tmp); if (cv == 0) { cerr << get_fileline() << ": error: queue '" << name_ << "' bound must be a constant!" << endl; des->errors += 1; max_idx = -1; } else { verinum res = cv->value(); if (res.is_defined()) { max_idx = res.as_long(); if (max_idx < 0) { cerr << get_fileline() << ": error: queue '" << name_ << "' bound must be positive (" << max_idx << ")!" << endl; des->errors += 1; max_idx = -1; } } else { cerr << get_fileline() << ": error: queue '" << name_ << "' bound is undefined!" << endl; des->errors += 1; max_idx = -1; } } } else max_idx = -1; netdarray = new netqueue_t(vec, max_idx); continue; } // Cannot handle dynamic arrays/queues of arrays yet. ivl_assert(*this, netdarray==0); long index_l, index_r; evaluate_range(des, array_type_scope, this, *cur, index_l, index_r); if (abs(index_r - index_l) > warn_dimension_size) { cerr << get_fileline() << ": warning: Array dimension " "is greater than " << warn_dimension_size << "." << endl; } unpacked_dimensions.push_back(netrange_t(index_l, index_r)); } if (data_type_ == IVL_VT_REAL && !packed_dimensions.empty()) { cerr << get_fileline() << ": error: real "; if (wtype == NetNet::REG) cerr << "variable"; else cerr << "net"; cerr << " '" << name_ << "' cannot be declared as a vector, found a range " << packed_dimensions << "." << endl; des->errors += 1; return 0; } /* If the net type is supply0 or supply1, replace it with a simple wire with a pulldown/pullup with supply strength. In other words, transform: supply0 foo; to: wire foo; pulldown #(supply0) (foo); This reduces the backend burden, and behaves exactly the same. */ NetLogic*pull = 0; if (wtype == NetNet::SUPPLY0 || wtype == NetNet::SUPPLY1) { if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Generate a SUPPLY pull for the "; if (wtype == NetNet::SUPPLY0) cerr << "supply0"; else cerr << "supply1"; cerr << " net." << endl; } NetLogic::TYPE pull_type = (wtype==NetNet::SUPPLY1) ? NetLogic::PULLUP : NetLogic::PULLDOWN; pull = new NetLogic(scope, scope->local_symbol(), 1, pull_type, wid); pull->set_line(*this); pull->pin(0).drive0(IVL_DR_SUPPLY); pull->pin(0).drive1(IVL_DR_SUPPLY); des->add_node(pull); wtype = NetNet::WIRE; } NetNet*sig = 0; if (class_type_t*class_type = dynamic_cast(set_data_type_)) { // If this is a class variable, then the class type // should already have been elaborated. All we need to // do right now is locate the netclass_t object for the // class, and use that to build the net. ivl_assert(*this, class_type->save_elaborated_type); netclass_t*use_type = class_type->save_elaborated_type; sig = new NetNet(scope, name_, wtype, unpacked_dimensions, use_type); } else if (struct_type_t*struct_type = dynamic_cast(set_data_type_)) { // If this is a struct type, then build the net with the // struct type. ivl_type_s*tmp_type = struct_type->elaborate_type(des, scope); netstruct_t*use_type = dynamic_cast(tmp_type); if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype; if (use_type->packed()) cerr << " " << use_type->packed_width() << " bit packed struct "; else cerr << " struct <> "; cerr << name_; cerr << " in scope " << scope_path(scope) << endl; } sig = new NetNet(scope, name_, wtype, unpacked_dimensions, use_type); } else if (enum_type_t*enum_type = dynamic_cast(set_data_type_)) { list::const_iterator sample_name = enum_type->names->begin(); const netenum_t*use_enum = base_type_scope->find_enumeration_for_name(des, sample_name->name); if (debug_elaborate) { cerr << get_fileline() << ": " << __func__ << ": " << "Create signal " << wtype << " enumeration " << name_ << " in scope " << scope_path(scope) << " with packed_dimensions=" << packed_dimensions << " and packed_width=" << use_enum->packed_width() << endl; } ivl_assert(*this, packed_dimensions.empty()); sig = new NetNet(scope, name_, wtype, unpacked_dimensions, use_enum); } else if (netdarray) { if (debug_elaborate) { cerr << get_fileline() << ": " << __func__ << ": " << "Create signal " << wtype << " dynamic array " << name_ << " in scope " << scope_path(scope) << endl; } ivl_assert(*this, packed_dimensions.empty()); ivl_assert(*this, unpacked_dimensions.empty()); sig = new NetNet(scope, name_, wtype, netdarray); } else if (dynamic_cast(set_data_type_)) { // Signal declared as: string foo; if (debug_elaborate) { cerr << get_fileline() << ": " << __func__ << ": " << "Create signal " << wtype << " string " << name_ << " in scope " << scope_path(scope) << 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 // a lot like packed dimensions from other means. // The trick here is that the parray type has an // arbitrary sub-type, and not just a scalar bit... netparray_t*use_type = elaborate_parray_type(des, scope, this, parray_type); // Should not be getting packed dimensions other than // through the parray type declaration. ivl_assert(*this, packed_dimensions.empty()); if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype << " parray=" << use_type->static_dimensions() << " " << name_ << unpacked_dimensions << " in scope " << scope_path(scope) << endl; } sig = new NetNet(scope, name_, wtype, unpacked_dimensions, use_type); } else { if (debug_elaborate) { cerr << get_fileline() << ": " << __func__ << ": " << "Create signal " << wtype << " data_type=" << data_type_; if (!get_scalar()) { cerr << " " << packed_dimensions; } cerr << " " << name_ << unpacked_dimensions; cerr << " in scope " << scope_path(scope) << endl; } ivl_variable_type_t use_data_type = data_type_; if (use_data_type == IVL_VT_NO_TYPE) { use_data_type = IVL_VT_LOGIC; if (debug_elaborate) { cerr << get_fileline() << ": " << __func__ << ": " << "Signal " << name_ << " in scope " << scope_path(scope) << " defaults to data type " << use_data_type << endl; } } netvector_t*vec = new netvector_t(packed_dimensions, use_data_type); vec->set_signed(get_signed()); vec->set_isint(get_isint()); if (is_implicit_scalar) vec->set_scalar(true); else vec->set_scalar(get_scalar()); packed_dimensions.clear(); sig = new NetNet(scope, name_, wtype, unpacked_dimensions, vec); } if (wtype == NetNet::WIRE) sig->devirtualize_pins(); sig->set_line(*this); sig->port_type(port_type_); if (ivl_discipline_t dis = get_discipline()) { sig->set_discipline(dis); } if (pull) connect(sig->pin(0), pull->pin(0)); for (unsigned idx = 0 ; idx < nattrib ; idx += 1) sig->attribute(attrib_list[idx].key, attrib_list[idx].val); return sig; }