From e01358babb755d50024c18fb545f1d7db24da581 Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sun, 13 Mar 2011 13:29:42 +0000 Subject: [PATCH] Fix for pr3194155. Currently the compiler coerces input ports to inout ports whenever there is an internal driver connected to the internal port net. This generates an error if the port is externally connected to something other than a structural net. This patch modifies the compiler to ensure port coercion only occurs in valid cases. --- PExpr.cc | 5 ++++ PExpr.h | 9 ++++++ elab_net.cc | 83 ++++++++++++++++++++++++++++++++++++++++------------ elaborate.cc | 9 ++++++ 4 files changed, 87 insertions(+), 19 deletions(-) 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.