diff --git a/PExpr.cc b/PExpr.cc index 36c990fff..152e6ebf4 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -71,6 +71,11 @@ NetNet* PExpr::elaborate_bi_net(Design*, NetScope*) const return 0; } +bool PExpr::is_collapsible_net(Design*, NetScope*) const +{ + return false; +} + PEBinary::PEBinary(char op, PExpr*l, PExpr*r) : op_(op), left_(l), right_(r) { diff --git a/PExpr.h b/PExpr.h index 500ea8dce..337305e6b 100644 --- a/PExpr.h +++ b/PExpr.h @@ -148,6 +148,12 @@ class PExpr : public LineInfo { // evaluated, return 0. virtual verinum* eval_const(Design*des, NetScope*sc) const; + // This method returns true if the expression represents a + // structural net that can have multiple drivers. This is + // used to test whether an input port connection can be + // collapsed to a single wire. + virtual bool is_collapsible_net(Design*des, NetScope*scope) const; + // This method returns true if that expression is the same as // this expression. This method is used for comparing // expressions that must be structurally "identical". @@ -193,6 +199,7 @@ class PEConcat : public PExpr { virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_force) const; + virtual bool is_collapsible_net(Design*des, NetScope*scope) const; private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, bool bidirectional_flag) const; @@ -302,6 +309,8 @@ class PEIdent : public PExpr { verinum* eval_const(Design*des, NetScope*sc) const; + virtual bool is_collapsible_net(Design*des, NetScope*scope) const; + const pform_name_t& path() const { return path_; } private: diff --git a/elab_net.cc b/elab_net.cc index 33feee1c1..88b5088c9 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -73,15 +73,17 @@ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope, nets[idx] = parms_[idx]->elaborate_lnet(des, scope); } - if (nets[idx] == 0) errors += 1; - else if (nets[idx]->data_type() == IVL_VT_REAL) { + if (nets[idx] == 0) { + errors += 1; + } else if (nets[idx]->data_type() == IVL_VT_REAL) { cerr << parms_[idx]->get_fileline() << ": error: " << "concatenation operand can no be real: " << *parms_[idx] << endl; errors += 1; continue; - } else width += nets[idx]->vector_width(); - + } else { + width += nets[idx]->vector_width(); + } } if (errors) { @@ -165,6 +167,28 @@ NetNet* PEConcat::elaborate_bi_net(Design*des, NetScope*scope) const return elaborate_lnet_common_(des, scope, true); } +bool PEConcat::is_collapsible_net(Design*des, NetScope*scope) const +{ + assert(scope); + + // Repeat concatenations are not currently supported. + if (repeat_) + return false; + + // Test the operands of the concatenation. + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { + + // Empty expressions are not allowed in concatenations + if (parms_[idx] == 0) + return false; + + if (!parms_[idx]->is_collapsible_net(des, scope)) + return false; + } + + return true; +} + /* * This private method evaluates the part selects (if any) for the * signal. The sig argument is the NetNet already located for the @@ -428,21 +452,6 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, return 0; } - if (sig->port_type() == NetNet::PINPUT) { - sig->port_type(NetNet::PINOUT); - // This map mask prevents an error message being - // repeated endlessly. - static map mask_map; - bool&flag = mask_map[sig->get_fileline() + ":" + string(sig->name())]; - if (! flag) { - cerr << get_fileline() << ": warning: L-value ``" - << sig->name() << "'' is also an input port." << endl; - cerr << sig->get_fileline() << ": warning: input " - << sig->name() << "; is coerced to inout." << endl; - flag = true; - } - } - // Default part select is the entire word. unsigned midx = sig->vector_width()-1, lidx = 0; // The default word select is the first. @@ -739,3 +748,39 @@ NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const scope->add_module_port(sig); return sig; } + +bool PEIdent::is_collapsible_net(Design*des, NetScope*scope) const +{ + assert(scope); + + NetNet* sig = 0; + const NetExpr*par = 0; + NetEvent* eve = 0; + + symbol_search(this, des, scope, path_, sig, par, eve); + + if (eve != 0) + return false; + + if (sig == 0) + return false; + + assert(sig); + + /* If this is SystemVerilog and the variable is not yet + assigned by anything, then convert it to an unresolved + wire. */ + if (gn_var_can_be_uwire() + && (sig->type() == NetNet::REG) + && (sig->peek_eref() == 0) ) { + sig->type(NetNet::UNRESOLVED_WIRE); + } + + if (sig->type() == NetNet::UNRESOLVED_WIRE && sig->pin(0).is_linked()) + return false; + + if (sig->type() == NetNet::REG) + return false; + + return true; +} diff --git a/elaborate.cc b/elaborate.cc index 5ef8c3736..c4fc18421 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -1306,6 +1306,15 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const if (instance.size() != 1) desired_vector_width = 0; + if (!prts.empty() && (prts[0]->port_type() == NetNet::PINPUT) + && prts[0]->pin(0).nexus()->drivers_present() + && pins[idx]->is_collapsible_net(des, scope)) { + prts[0]->port_type(NetNet::PINOUT); + + cerr << pins[idx]->get_fileline() << ": warning: input port " + << prts[0]->name() << " is coerced to inout." << endl; + } + // Elaborate the expression that connects to the // module[s] port. sig is the thing outside the module // that connects to the port.