/* * Copyright (c) 2000-2011 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ # include "config.h" # include # include # include "Module.h" # include "PExpr.h" # include "PGate.h" # include "PGenerate.h" # include "PTask.h" # include "PWire.h" # include "Statement.h" # include "compiler.h" # include "netlist.h" # include "netmisc.h" # include "util.h" # include "ivl_assert.h" 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; } 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 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; } (*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); } } 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); // 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; } 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->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); /* 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 (ports_ == 0 && !gn_system_verilog()) { cerr << get_fileline() << ": error: Function " << fname << " has no ports." << endl; cerr << get_fileline() << ": : Functions must have" << " at least one input port." << endl; des->errors += 1; } NetNet*ret_sig = 0; /* Create the signals/variables of the return value and write them into the function scope. */ switch (return_type_.type) { case PTF_REG: case PTF_REG_S: if (return_type_.range) { ivl_assert(*this, return_type_.range->size() == 2); NetExpr*me = elab_and_eval(des, scope, return_type_.range->at(0), -1, true); assert(me); NetExpr*le = elab_and_eval(des, scope, return_type_.range->at(1), -1, true); assert(le); long mnum = 0, lnum = 0; if ( ! get_const_argument(me, mnum) ) { cerr << me->get_fileline() << ": error: " "Unable to evaluate constant expression " << *me << "." << endl; des->errors += 1; } if ( ! get_const_argument(le, lnum) ) { cerr << le->get_fileline() << ": error: " "Unable to evaluate constant expression " << *le << "." << endl; des->errors += 1; } ret_sig = new NetNet(scope, fname, NetNet::REG, mnum, lnum); ret_sig->set_scalar(false); } else { ret_sig = new NetNet(scope, fname, NetNet::REG); ret_sig->set_scalar(true); } ret_sig->set_line(*this); ret_sig->set_signed(return_type_.type == PTF_REG_S); ret_sig->port_type(NetNet::POUTPUT); ret_sig->data_type(IVL_VT_LOGIC); break; case PTF_INTEGER: ret_sig = new NetNet(scope, fname, NetNet::REG, integer_width); ret_sig->set_line(*this); ret_sig->set_signed(true); ret_sig->set_isint(true); ret_sig->set_scalar(false); ret_sig->port_type(NetNet::POUTPUT); ret_sig->data_type(IVL_VT_LOGIC); break; case PTF_TIME: ret_sig = new NetNet(scope, fname, NetNet::REG, 64); ret_sig->set_line(*this); ret_sig->set_signed(false); ret_sig->set_isint(false); ret_sig->set_scalar(false); ret_sig->port_type(NetNet::POUTPUT); ret_sig->data_type(IVL_VT_LOGIC); break; case PTF_REAL: case PTF_REALTIME: ret_sig = new NetNet(scope, fname, NetNet::REG, 1); ret_sig->set_line(*this); ret_sig->set_signed(true); ret_sig->set_isint(false); ret_sig->set_scalar(true); ret_sig->port_type(NetNet::POUTPUT); ret_sig->data_type(IVL_VT_REAL); break; case PTF_ATOM2: case PTF_ATOM2_S: ivl_assert(*this, return_type_.range != 0); long use_wid; { NetExpr*me = elab_and_eval(des, scope, (*return_type_.range)[0], -1, true); assert(me); NetExpr*le = elab_and_eval(des, scope, (*return_type_.range)[1], -1, true); assert(le); long mnum = 0, lnum = 0; if ( ! get_const_argument(me, mnum) ) { cerr << me->get_fileline() << ": error: " "Unable to evaluate constant expression " << *me << "." << endl; des->errors += 1; } if ( ! get_const_argument(le, lnum) ) { cerr << le->get_fileline() << ": error: " "Unable to evaluate constant expression " << *le << "." << endl; des->errors += 1; } use_wid = mnum - lnum + 1; } ret_sig = new NetNet(scope, fname, NetNet::REG, use_wid); ret_sig->set_line(*this); ret_sig->set_signed(return_type_.type == PTF_ATOM2_S? true : false); ret_sig->set_isint(true); ret_sig->set_scalar(false); ret_sig->port_type(NetNet::POUTPUT); ret_sig->data_type(IVL_VT_BOOL); break; default: if (ports_) { cerr << get_fileline() << ": internal error: I don't know " << "how to deal with return type of function " << scope->basename() << "." << endl; } else { /* If we do not have any ports or a return type this * is probably a bad function definition. */ cerr << get_fileline() << ": error: Bad definition for " << "function " << scope->basename() << "?" << endl; return; } } svectorports (ports_? ports_->count() : 0); if (ports_) for (unsigned idx = 0 ; idx < ports_->count() ; idx += 1) { /* Parse the port name into the task name and the reg name. We know by design that the port name is given as two components: .. */ perm_string pname = (*ports_)[idx]->basename(); NetNet*tmp = scope->find_signal(pname); ports[idx] = 0; if (tmp == 0) { cerr << get_fileline() << ": internal error: function " << scope_path(scope) << " is missing port " << pname << "." << endl; scope->dump(cerr); cerr << get_fileline() << ": Continuing..." << endl; des->errors += 1; continue; } if (tmp->port_type() == NetNet::NOT_A_PORT) { cerr << get_fileline() << ": internal error: function " << scope_path(scope) << " port " << pname << " is a port but is not a port?" << endl; des->errors += 1; scope->dump(cerr); continue; } ports[idx] = tmp; if (tmp->port_type() != NetNet::PINPUT) { cerr << tmp->get_fileline() << ": error: function " << scope_path(scope) << " port " << pname << " is not an input port." << endl; cerr << tmp->get_fileline() << ": : Function arguments " << "must be input ports." << endl; des->errors += 1; } } NetFuncDef*def = 0; if (ret_sig) def = new NetFuncDef(scope, ret_sig, ports); assert(def); if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Attach function definition to scope " << scope_path(scope) << "." << 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); svectorports (ports_? ports_->count() : 0); for (unsigned idx = 0 ; idx < ports.count() ; idx += 1) { perm_string port_name = (*ports_)[idx]->basename(); /* Find the signal for the port. We know by definition that it is in the scope of the task, so look only in the scope. */ NetNet*tmp = scope->find_signal(port_name); if (tmp == 0) { cerr << get_fileline() << ": internal error: " << "Could not find port " << port_name << " in scope " << scope_path(scope) << endl; scope->dump(cerr); des->errors += 1; } ports[idx] = tmp; } NetTaskDef*def = new NetTaskDef(scope, ports); scope->set_task_def(def); // Look for further signals in the sub-statement if (statement_) statement_->elaborate_sig(des, scope); } 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 PEventStatement::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); } /* * 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 { NetNet::Type wtype = type_; bool is_implicit_scalar = false; if (wtype == NetNet::IMPLICIT) { wtype = NetNet::WIRE; is_implicit_scalar = true; } if (wtype == NetNet::IMPLICIT_REG) { wtype = NetNet::REG; is_implicit_scalar = true; } unsigned wid = 1; long lsb = 0, msb = 0; des->errors += error_cnt_; // A signal can not have the same name as a scope object. const NetScope *child = scope->child(hname_t(name_)); if (child) { cerr << get_fileline() << ": error: signal and "; child->print_type(cerr); cerr << " in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } // A signal can not have the same name as a genvar. const LineInfo *genvar = scope->find_genvar(name_); if (genvar) { cerr << get_fileline() << ": error: signal and genvar in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } // A signal can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter(des, name_, ex_msb, ex_lsb); if (parm) { cerr << get_fileline() << ": error: signal and parameter in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } // A signal can not have the same name as a named event. const NetEvent *event = scope->find_event(name_); if (event) { cerr << get_fileline() << ": error: signal and named event in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; } if (port_set_ || net_set_) { long pmsb = 0, plsb = 0, nmsb = 0, nlsb = 0; bool bad_lsb = false, bad_msb = false; /* If they exist get the port definition MSB and LSB */ if (port_set_ && port_msb_ != 0) { NetExpr*texpr = elab_and_eval(des, scope, port_msb_, -1, true); if (! eval_as_long(pmsb, texpr)) { cerr << port_msb_->get_fileline() << ": error: " "Range expressions must be constant." << endl; cerr << port_msb_->get_fileline() << " : " "This MSB expression violates the rule: " << *port_msb_ << endl; des->errors += 1; bad_msb = true; } delete texpr; texpr = elab_and_eval(des, scope, port_lsb_, -1, true); if (! eval_as_long(plsb, texpr)) { cerr << port_lsb_->get_fileline() << ": error: " "Range expressions must be constant." << endl; cerr << port_lsb_->get_fileline() << " : " "This LSB expression violates the rule: " << *port_lsb_ << endl; des->errors += 1; bad_lsb = true; } delete texpr; nmsb = pmsb; nlsb = plsb; /* An implicit port can have a range so note that here. */ is_implicit_scalar = false; } if (!port_set_) assert(port_msb_ == 0 && port_lsb_ == 0); if (port_msb_ == 0) assert(port_lsb_ == 0); if (port_lsb_ == 0) assert(port_msb_ == 0); /* If they exist get the net/etc. definition MSB and LSB */ if (net_set_ && net_msb_ != 0 && !bad_msb && !bad_lsb) { NetExpr*texpr = elab_and_eval(des, scope, net_msb_, -1, true); if (! eval_as_long(nmsb, texpr)) { cerr << net_msb_->get_fileline() << ": error: " "Range expressions must be constant." << endl; cerr << net_msb_->get_fileline() << " : " "This MSB expression violates the rule: " << *net_msb_ << endl; des->errors += 1; bad_msb = true; } delete texpr; texpr = elab_and_eval(des, scope, net_lsb_, -1, true); if (! eval_as_long(nlsb, texpr)) { cerr << net_lsb_->get_fileline() << ": error: " "Range expressions must be constant." << endl; cerr << net_lsb_->get_fileline() << " : " "This LSB expression violates the rule: " << *net_lsb_ << endl; des->errors += 1; bad_lsb = true; } delete texpr; } if (!net_set_) assert(net_msb_ == 0 && net_lsb_ == 0); if (net_msb_ == 0) assert(net_lsb_ == 0); if (net_lsb_ == 0) assert(net_msb_ == 0); /* We have a port size error */ if (port_set_ && net_set_ && (pmsb != nmsb || plsb != nlsb)) { /* Scalar port with a vector net/etc. definition */ if (port_msb_ == 0) { if (!gn_io_range_error_flag) { cerr << get_fileline() << ": warning: Scalar port ``" << name_ << "'' has a vectored net declaration [" << nmsb << ":" << nlsb << "]." << endl; } else { cerr << get_fileline() << ": error: Scalar port ``" << name_ << "'' has a vectored net declaration [" << nmsb << ":" << nlsb << "]." << endl; des->errors += 1; return 0; } } /* Vectored port with a scalar net/etc. definition */ if (net_msb_ == 0) { cerr << port_msb_->get_fileline() << ": error: Vectored port ``" << name_ << "'' [" << pmsb << ":" << plsb << "] has a scalar net declaration at " << get_fileline() << "." << endl; des->errors += 1; return 0; } /* Both vectored, but they have different ranges. */ if (port_msb_ != 0 && net_msb_ != 0) { cerr << port_msb_->get_fileline() << ": error: Vectored port ``" << name_ << "'' [" << pmsb << ":" << plsb << "] has a net declaration [" << nmsb << ":" << nlsb << "] at " << net_msb_->get_fileline() << " that does not match." << endl; des->errors += 1; return 0; } } /* Attempt to recover from errors. */ if (bad_lsb) nlsb = 0; if (bad_msb) nmsb = nlsb; lsb = nlsb; msb = nmsb; if (nmsb > nlsb) wid = nmsb - nlsb + 1; else wid = nlsb - nmsb + 1; } unsigned nattrib = 0; attrib_list_t*attrib_list = evaluate_attributes(attributes, nattrib, des, scope); long array_s0 = 0; long array_e0 = 0; unsigned array_dimensions = 0; /* If the ident has idx expressions, then this is a memory. It can only have the idx registers after the msb and lsb expressions are filled. And, if it has one index, it has both. */ if (lidx_ || ridx_) { assert(lidx_ && ridx_); NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1, true); NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1, true); if ((lexp == 0) || (rexp == 0)) { cerr << get_fileline() << ": internal error: There is " << "a problem evaluating indices for ``" << name_ << "''." << endl; des->errors += 1; return 0; } bool const_flag = true; verinum lval, rval; const_flag &= get_const_argument(lexp, lval); const_flag &= get_const_argument(rexp, rval); delete rexp; delete lexp; if (!const_flag) { cerr << get_fileline() << ": error: The indices " << "are not constant for array ``" << name_ << "''." << endl; des->errors += 1; /* Attempt to recover from error, */ array_s0 = 0; array_e0 = 0; } else { array_s0 = lval.as_long(); array_e0 = rval.as_long(); } array_dimensions = 1; } if (data_type_ == IVL_VT_REAL && (msb != 0 || lsb != 0)) { 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 [" << msb << ":" << lsb << "]." << 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) { 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; 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; } } if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype; if (!get_scalar()) { cerr << " ["< 0) { cerr << " [" << array_s0 << ":" << array_e0 << "]" << endl; } cerr << " in scope " << scope_path(scope) << endl; } NetNet*sig = array_dimensions > 0 ? new NetNet(scope, name_, wtype, msb, lsb, array_s0, array_e0) : new NetNet(scope, name_, wtype, msb, lsb); // If this is an enumeration, then set the enumeration set for // the new signal. This turns it into an enumeration. if (enum_type_) { ivl_assert(*this, ! enum_type_->names->empty()); list::const_iterator sample_name = enum_type_->names->begin(); netenum_t*use_enum = scope->enumeration_for_name(sample_name->name); sig->set_enumeration(use_enum); } if (wtype == NetNet::WIRE) sig->devirtualize_pins(); 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() << ": debug: " << "Signal " << name_ << " in scope " << scope_path(scope) << " defaults to data type " << use_data_type << endl; } } sig->data_type(use_data_type); sig->set_line(*this); sig->port_type(port_type_); sig->set_signed(get_signed()); sig->set_isint(get_isint()); if (is_implicit_scalar) sig->set_scalar(true); else sig->set_scalar(get_scalar()); 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; }