From dfb7bf52115e39dc53d48e71847acc9767f1cc47 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Fri, 9 May 2008 17:42:37 -0700 Subject: [PATCH] Handle part selects of nets that fall of the ends of the identifier. The Verilog LRM specifies that it is legal to give constant part selects that are beyond the bounds of the identifier being selected. But elaboration was flagging that as an error. This patch changes it to a warning, and handles the cases by generating 'bx bits as needed. --- PExpr.h | 2 +- elab_net.cc | 138 +++++++++++++++++++++++++++++++++++++++++++++------- netlist.cc | 2 +- netlist.h | 2 +- 4 files changed, 123 insertions(+), 21 deletions(-) diff --git a/PExpr.h b/PExpr.h index 889f53f48..0d0b66f33 100644 --- a/PExpr.h +++ b/PExpr.h @@ -387,7 +387,7 @@ class PEIdent : public PExpr { NetNet*make_implicit_net_(Design*des, NetScope*scope) const; bool eval_part_select_(Design*des, NetScope*scope, NetNet*sig, - unsigned&midx, unsigned&lidx) const; + long&midx, long&lidx) const; NetNet*process_select_(Design*des, NetScope*scope, NetNet*sig) const; }; diff --git a/elab_net.cc b/elab_net.cc index a1446b9c6..97fcb76cd 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -1894,10 +1894,11 @@ NetNet* PEIdent::process_select_(Design*des, NetScope*scope, // dimensions, then treat them as word part selects. For // example, if this is a memory array, then array dimensions // is the first and part select the remainder. - unsigned midx, lidx; + long midx, lidx; if (! eval_part_select_(des, scope, sig, midx, lidx)) return sig; + ivl_assert(*this, lidx >= 0); unsigned part_count = midx-lidx+1; // Maybe this is a full-width constant part select? If @@ -1963,12 +1964,70 @@ NetNet* PEIdent::elaborate_net_net_(Design*des, NetScope*scope, delete mval; } - unsigned midx, lidx; + long midx, lidx; if (! eval_part_select_(des, scope, sig, midx, lidx)) return 0; unsigned part_count = midx-lidx+1; + unsigned output_width = part_count; + /* Detect and handle the special case that the entire part + select is outside the range of the signal. Return a + constant xxx. */ + if (midx < 0 || lidx >= (long)sig->vector_width()) { + ivl_assert(*this, sig->data_type() == IVL_VT_LOGIC); + verinum xxx (verinum::Vx, part_count); + NetConst*con = new NetConst(scope, scope->local_symbol(), xxx); + con->set_line(*sig); + des->add_node(con); + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, part_count-1, 0); + tmp->data_type( sig->data_type() ); + tmp->local_flag(true); + connect(tmp->pin(0), con->pin(0)); + return tmp; + } + + NetNet*below = 0; + if (lidx < 0) { + ivl_assert(*this, sig->data_type() == IVL_VT_LOGIC); + unsigned xxx_wid = 0-lidx; + verinum xxx (verinum::Vx, xxx_wid); + NetConst*con = new NetConst(scope, scope->local_symbol(), xxx); + con->set_line(*sig); + des->add_node(con); + + below = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, xxx_wid); + below->data_type( sig->data_type() ); + below->local_flag(true); + connect(below->pin(0), con->pin(0)); + + lidx = 0; + part_count = midx-lidx+1; + } + + NetNet*above = 0; + if (midx >= (long)sig->vector_width()) { + ivl_assert(*this, sig->data_type() == IVL_VT_LOGIC); + unsigned xxx_wid = midx - sig->vector_width() + 1; + verinum xxx (verinum::Vx, xxx_wid); + NetConst*con = new NetConst(scope, scope->local_symbol(), xxx); + con->set_line(*sig); + des->add_node(con); + + above = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, xxx_wid); + above->data_type( sig->data_type() ); + above->local_flag(true); + connect(above->pin(0), con->pin(0)); + + midx = sig->vector_width()-1; + part_count = midx-lidx+1; + } + + ivl_assert(*this, lidx >= 0); if (part_count != sig->vector_width()) { if (debug_elaborate) { cerr << get_fileline() << ": debug: Elaborate part select " @@ -1990,7 +2049,36 @@ NetNet* PEIdent::elaborate_net_net_(Design*des, NetScope*scope, sig = tmp; } + unsigned segment_count = 1; + if (below) segment_count += 1; + if (above) segment_count += 1; + if (segment_count > 1) { + NetConcat*cc = new NetConcat(scope, scope->local_symbol(), + output_width, segment_count); + cc->set_line(*sig); + des->add_node(cc); + NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, output_width); + tmp->data_type( sig->data_type() ); + tmp->local_flag(true); + connect(tmp->pin(0), cc->pin(0)); + + unsigned pdx = 1; + if (below) { + connect(cc->pin(pdx), below->pin(0)); + pdx += 1; + } + connect(cc->pin(pdx), sig->pin(0)); + pdx += 1; + if (above) { + connect(cc->pin(pdx), above->pin(0)); + pdx += 1; + } + ivl_assert(*sig, segment_count == pdx-1); + + sig = tmp; + } return sig; } @@ -2385,7 +2473,7 @@ NetNet* PEIdent::make_implicit_net_(Design*des, NetScope*scope) const * anything in between. The values are in canonical indices. */ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, - unsigned&midx, unsigned&lidx) const + long&midx, long&lidx) const { const name_component_t&name_tail = path_.back(); // Only treat as part/bit selects any index that is beyond the @@ -2449,31 +2537,36 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, long msb, lsb; /* bool flag = */ calculate_parts_(des, scope, msb, lsb); - lidx = sig->sb_to_idx(lsb); - midx = sig->sb_to_idx(msb); + long lidx_tmp = sig->sb_to_idx(lsb); + long midx_tmp = sig->sb_to_idx(msb); /* Detect reversed indices of a part select. */ - if (lidx > midx) { + if (lidx_tmp > midx_tmp) { cerr << get_fileline() << ": error: Part select " << sig->name() << "[" << msb << ":" << lsb << "] indices reversed." << endl; cerr << get_fileline() << ": : Did you mean " << sig->name() << "[" << lsb << ":" << msb << "]?" << endl; - unsigned tmp = midx; - midx = lidx; - lidx = tmp; + long tmp = midx_tmp; + midx_tmp = lidx_tmp; + lidx_tmp = tmp; des->errors += 1; } /* Detect a part select out of range. */ - if (midx >= sig->vector_width()) { - cerr << get_fileline() << ": error: Part select " + if (midx_tmp >= (long)sig->vector_width() || lidx_tmp < 0) { + cerr << get_fileline() << ": warning: Part select " << sig->name() << "[" << msb << ":" << lsb << "] out of range." << endl; - midx = sig->vector_width() - 1; - lidx = 0; +#if 0 + midx_tmp = sig->vector_width() - 1; + lidx_tmp = 0; des->errors += 1; +#endif } + + midx = midx_tmp; + lidx = lidx_tmp; break; } @@ -2494,7 +2587,7 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, assert(mval); midx = sig->sb_to_idx(mval->as_long()); - if (midx >= sig->vector_width()) { + if (midx >= (long)sig->vector_width()) { cerr << get_fileline() << ": error: Index " << sig->name() << "[" << mval->as_long() << "] out of range." << endl; @@ -2610,12 +2703,21 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, /* The array has a part/bit select at the end. */ if (name_tail.index.size() > sig->array_dimensions()) { - if (! eval_part_select_(des, scope, sig, midx, lidx)) + long midx_tmp, lidx_tmp; + if (! eval_part_select_(des, scope, sig, midx_tmp, lidx_tmp)) return 0; + ivl_assert(*this, lidx_tmp >= 0); + midx = midx_tmp; + lidx = lidx_tmp; } } else if (!name_tail.index.empty()) { - if (! eval_part_select_(des, scope, sig, midx, lidx)) + long midx_tmp, lidx_tmp; + if (! eval_part_select_(des, scope, sig, midx_tmp, lidx_tmp)) return 0; + + ivl_assert(*this, lidx_tmp >= 0); + midx = midx_tmp; + lidx = lidx_tmp; } unsigned subnet_wid = midx-lidx+1; @@ -2737,8 +2839,8 @@ NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const return 0; } - unsigned midx; - unsigned lidx; + long midx; + long lidx; /* Evaluate the part/bit select expressions, to get the part select of the signal that attaches to the port. Also handle diff --git a/netlist.cc b/netlist.cc index 45fee8f34..22c9f03ee 100644 --- a/netlist.cc +++ b/netlist.cc @@ -648,7 +648,7 @@ bool NetNet::sb_is_valid(long sb) const return (sb <= lsb_) && (sb >= msb_); } -unsigned NetNet::sb_to_idx(long sb) const +long NetNet::sb_to_idx(long sb) const { if (msb_ >= lsb_) return sb - lsb_; diff --git a/netlist.h b/netlist.h index 38859ccf8..afd3c89b1 100644 --- a/netlist.h +++ b/netlist.h @@ -510,7 +510,7 @@ class NetNet : public NetObj { /* This method converts a signed index (the type that might be found in the Verilog source) to a pin number. It accounts for variation in the definition of the reg/wire/whatever. */ - unsigned sb_to_idx(long sb) const; + long sb_to_idx(long sb) const; /* This method checks that the signed index is valid for this signal. If it is, the above sb_to_idx can be used to get