@@ -5437,6 +5437,13 @@ ivl_type_t PEIdent::resolve_type_(Design *des, const symbol_search_results &sr,
54375437 if (index_depth < array_size)
54385438 return type;
54395439
5440+ // A part select of a packed array selects bits, not
5441+ // an element
5442+ auto last = std::next (index, array_size - 1 );
5443+ if (dynamic_cast <const netparray_t *>(array)
5444+ && last->sel != index_component_t ::SEL_BIT )
5445+ return type;
5446+
54405447 index_depth -= array_size;
54415448 while (array_size--)
54425449 index++;
@@ -5766,7 +5773,6 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode)
57665773 && dynamic_cast <const netsarray_t *>(sr.type )
57675774 && !dynamic_cast <const netparray_t *>(sr.type );
57685775 bool packed_elem_sel = use_sel == index_component_t ::SEL_BIT
5769- && name_tail.index .size () == 1
57705776 && dynamic_cast <const netparray_t *>(sr.type );
57715777 if (!unpacked_elem_sel && !packed_elem_sel)
57725778 return test_width_parameter_ (sr.par_val , mode);
@@ -6649,6 +6655,51 @@ static NetExpr* scale_expr_by_const(NetExpr*expr, unsigned long val)
66496655 return res;
66506656}
66516657
6658+ /*
6659+ * The last index of a select of a multi-dimension packed array parameter
6660+ * applies to the dimension after the prefix indices, as it does for a
6661+ * variable. Return the range of that dimension, the width of each of its
6662+ * elements, and the bit offset of the slice that the prefix indices select.
6663+ * Any other parameter has only the range that calculate_param_range() finds.
6664+ */
6665+ static bool calculate_param_slice (const LineInfo&line, Design*des,
6666+ NetScope*scope, ivl_type_t par_type,
6667+ long length,
6668+ const list<index_component_t >&indices,
6669+ long &par_msv, long &par_lsv,
6670+ long &slice_off, unsigned long &slice_wid)
6671+ {
6672+ slice_off = 0 ;
6673+ slice_wid = 1 ;
6674+ if (!par_type || !par_type->packed ()
6675+ || par_type->slice_dimensions ().size () < 2 )
6676+ return calculate_param_range (line, par_type, par_msv, par_lsv,
6677+ length);
6678+
6679+ // Every index but the last is a constant prefix that picks one slice,
6680+ // exactly as for a variable.
6681+ list<long > prefix_indices;
6682+ if (!evaluate_index_prefix (des, scope, prefix_indices, indices))
6683+ return false ;
6684+
6685+ const netranges_t dims = par_type->slice_dimensions ();
6686+ if (prefix_indices.size () >= dims.size ()) {
6687+ cerr << line.get_fileline () << " : error: the number of indices ("
6688+ << indices.size ()
6689+ << " ) is greater than the number of dimensions ("
6690+ << dims.size ()
6691+ << " )." << endl;
6692+ des->errors += 1 ;
6693+ return false ;
6694+ }
6695+ // The last index selects within the dimension after the prefix.
6696+ const netrange_t &last_dim = dims[prefix_indices.size ()];
6697+ par_msv = last_dim.get_msb ();
6698+ par_lsv = last_dim.get_lsb ();
6699+ return prefix_to_slice (dims, prefix_indices, par_lsv, slice_off,
6700+ slice_wid);
6701+ }
6702+
66526703/*
66536704 * Handle an index select of a multi-dimension packed array parameter, e.g.
66546705 *
@@ -6659,9 +6710,9 @@ static NetExpr* scale_expr_by_const(NetExpr*expr, unsigned long val)
66596710 * 4-bit slice), not a single bit. The parameter value is a flat packed
66606711 * constant, so this becomes a fixed-width part select of the flat value: for a
66616712 * constant index we fold it directly, otherwise we build an indexed part
6662- * select over a NetEConstParam. Only the outermost dimension is indexed here
6663- * ( the common ROM/LUT case, `name[idx]`) ; the selected element keeps the
6664- * remaining dimensions as its width.
6713+ * select over a NetEConstParam. For a packed array, slice_off is the bit
6714+ * offset of the slice that any prefix indices select ; the selected element
6715+ * keeps the remaining dimensions as its width.
66656716 *
66666717 * The flat bit layout differs between packed and unpacked array parameters:
66676718 * - packed (packed_layout true): the value comes from a concatenation, so
@@ -6677,7 +6728,8 @@ NetExpr* PEIdent::elaborate_expr_param_slice_(const NetEConst*par_ex,
66776728 unsigned long elem_w,
66786729 bool packed_layout,
66796730 NetExpr*sel,
6680- perm_string name) const
6731+ perm_string name,
6732+ long slice_off) const
66816733{
66826734 long min_idx = (msb0 <= lsb0) ? msb0 : lsb0;
66836735 long max_idx = (msb0 <= lsb0) ? lsb0 : msb0;
@@ -6690,18 +6742,19 @@ NetExpr* PEIdent::elaborate_expr_param_slice_(const NetEConst*par_ex,
66906742
66916743 // Constant index: fold to the selected element bits directly.
66926744 if (const NetEConst*sel_c = dynamic_cast <NetEConst*>(sel)) {
6693- if (! sel_c->value ().is_defined ()) {
6694- NetEConst*res = new NetEConst (verinum (verinum::Vx, elem_w, true ));
6695- res->set_line (*this );
6696- return res;
6697- }
66986745 long sel_v = sel_c->value ().as_long ();
66996746 // Canonical element index counting from the LSB end of the flat
67006747 // value.
67016748 long cidx = packed_layout
67026749 ? ((msb0 >= lsb0) ? (sel_v - lsb0) : (lsb0 - sel_v))
67036750 : (sel_v - min_idx);
6704- long lsv = cidx * (long )elem_w;
6751+ bool index_in_range = cidx >= 0 && cidx <= max_idx - min_idx;
6752+ if (! sel_c->value ().is_defined () || ! index_in_range) {
6753+ NetEConst*res = new NetEConst (verinum (verinum::Vx, elem_w, true ));
6754+ res->set_line (*this );
6755+ return res;
6756+ }
6757+ long lsv = slice_off + cidx * (long )elem_w;
67056758 verinum result = param_part_select_bits (par_ex->value (), elem_w, lsv);
67066759 NetEConst*res = new NetEConst (result);
67076760 res->set_line (*this );
@@ -6710,9 +6763,12 @@ NetExpr* PEIdent::elaborate_expr_param_slice_(const NetEConst*par_ex,
67106763
67116764 // Variable index: build an indexed part select over the flat value.
67126765 // Normalize the outer index to a canonical element index, then scale
6713- // it up to a bit offset by the element width.
6766+ // it up to a bit offset by the element width. The prefix indices
6767+ // already moved the start of the slice by slice_off bits.
6768+ long slice_off_elems = slice_off / (long )elem_w;
67146769 NetExpr*base = packed_layout
6715- ? normalize_variable_base (sel, msb0, lsb0, elem_w, msb0 > lsb0)
6770+ ? normalize_variable_base (sel, msb0, lsb0, elem_w, msb0 > lsb0,
6771+ slice_off_elems)
67166772 : normalize_variable_base (sel, max_idx, min_idx, elem_w, true );
67176773 base = scale_expr_by_const (base, elem_w);
67186774
@@ -6910,34 +6966,21 @@ NetExpr* PEIdent::elaborate_expr_param_bit_(Design*des, NetScope*scope,
69106966
69116967 // An index select of an array parameter selects a whole element, not
69126968 // a single bit. This applies to a multi-dimension packed array,
6913- // e.g. localparam logic [0:63][3:0] MEM = '{...}, to a packed array of
6914- // a typedef, e.g. localparam word_t [0:63] MEM = '{...}, and to an
6969+ // e.g. localparam logic [0:63][3:0] MEM = '{...}, and to an
69156970 // unpacked array, e.g. localparam logic [3:0] MEM [0:63] = '{...}
69166971 // (whose value has been flattened to the packed-equivalent constant).
69176972 // The selected element keeps the remaining dimensions as its width.
6918- if (const netvector_t *par_vec = dynamic_cast <const netvector_t *>(par_type)) {
6919- if (par_vec->packed_dims ().size () > 1 ) {
6920- const netranges_t &pdims = par_vec->packed_dims ();
6921- long msb0 = pdims[0 ].get_msb ();
6922- long lsb0 = pdims[0 ].get_lsb ();
6923- unsigned long outer_w =
6924- (msb0 >= lsb0) ? (unsigned long )(msb0 - lsb0 + 1 )
6925- : (unsigned long )(lsb0 - msb0 + 1 );
6926- unsigned long elem_w = par_vec->packed_width () / outer_w;
6927- return elaborate_expr_param_slice_ (par_ex, found_in,
6928- msb0, lsb0, elem_w,
6929- true , sel, name);
6930- }
6931- }
6932- if (const netparray_t *par_arr = dynamic_cast <const netparray_t *>(par_type)) {
6933- if (name_tail.index .size () == 1 ) {
6934- const netrange_t &outer = par_arr->static_dimensions ()[0 ];
6935- unsigned long elem_w = par_arr->packed_width () / outer.width ();
6936- return elaborate_expr_param_slice_ (par_ex, found_in,
6937- outer.get_msb (),
6938- outer.get_lsb (), elem_w,
6939- true , sel, name);
6940- }
6973+ if (par_type && par_type->packed ()
6974+ && par_type->slice_dimensions ().size () > 1 ) {
6975+ long msb0, lsb0, slice_off;
6976+ unsigned long elem_w;
6977+ if (!calculate_param_slice (*this , des, scope, par_type,
6978+ par_ex->value ().len (), name_tail.index ,
6979+ msb0, lsb0, slice_off, elem_w))
6980+ return 0 ;
6981+ return elaborate_expr_param_slice_ (par_ex, found_in, msb0, lsb0,
6982+ elem_w, true , sel, name,
6983+ slice_off);
69416984 }
69426985 if (const netsarray_t *par_arr = dynamic_cast <const netsarray_t *>(par_type)) {
69436986 if (!dynamic_cast <const netparray_t *>(par_arr)) {
@@ -7046,9 +7089,12 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope,
70467089 ivl_assert (*this , par_ex);
70477090
70487091
7049- long par_msv, par_lsv;
7050- if (! calculate_param_range (*this , par_type, par_msv, par_lsv,
7051- par_ex->value ().len ())) return 0 ;
7092+ long par_msv, par_lsv, slice_off;
7093+ unsigned long slice_wid;
7094+ if (! calculate_param_slice (*this , des, scope, par_type,
7095+ par_ex->value ().len (), path_.back ().index ,
7096+ par_msv, par_lsv, slice_off, slice_wid))
7097+ return 0 ;
70527098
70537099 if (! parts_defined_flag) {
70547100 if (warn_ob_select) {
@@ -7081,7 +7127,10 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope,
70817127 return 0 ;
70827128 }
70837129
7084- long wid = 1 + labs (msv-lsv);
7130+ // The range counts elements of the last dimension, each slice_wid
7131+ // bits wide.
7132+ long elem_count = 1 + labs (msv-lsv);
7133+ long wid = elem_count * slice_wid;
70857134
70867135 // Watch out for reversed bit numbering. We're making
70877136 // the part select from LSB to MSB.
@@ -7091,6 +7140,8 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope,
70917140 } else {
70927141 base = lsv - par_lsv;
70937142 }
7143+ // Convert the element offset into a bit offset within the slice.
7144+ base = slice_off + base * slice_wid;
70947145
70957146 if (warn_ob_select) {
70967147 if (base < 0 ) {
@@ -7165,15 +7216,21 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_do_(Design*des, NetScope*scope,
71657216 const NetEConst*par_ex = dynamic_cast <const NetEConst*> (par);
71667217 ivl_assert (*this , par_ex);
71677218
7168- long par_msv, par_lsv;
7169- if (! calculate_param_range (*this , par_type, par_msv, par_lsv,
7170- par_ex->value ().len ())) return 0 ;
7219+ long par_msv, par_lsv, slice_off;
7220+ unsigned long slice_wid;
7221+ if (! calculate_param_slice (*this , des, scope, par_type,
7222+ par_ex->value ().len (), path_.back ().index ,
7223+ par_msv, par_lsv, slice_off, slice_wid))
7224+ return 0 ;
71717225
71727226 NetExpr*base = calculate_up_do_base_ (des, scope, need_const);
71737227 if (base == 0 ) return 0 ;
71747228
71757229 // Use the part select width already calculated by test_width().
7230+ // The width in the select counts elements of the last dimension,
7231+ // each slice_wid bits wide.
71767232 unsigned long wid = min_width_;
7233+ unsigned long elem_count = wid / slice_wid;
71777234
71787235 perm_string name = peek_tail_name (path_);
71797236
@@ -7209,15 +7266,15 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_do_(Design*des, NetScope*scope,
72097266 long lsv = base_c->value ().as_long ();
72107267 long par_base = par_lsv;
72117268 if (!up)
7212- par_base += wid - 1 ;
7269+ par_base += elem_count - 1 ;
72137270
72147271 // Watch out for reversed bit numbering. We're making
72157272 // the part select from LSB to MSB.
72167273 if (par_msv < par_lsv) {
72177274 par_base = lsv;
72187275 lsv = par_lsv;
72197276 if (up)
7220- lsv -= wid - 1 ;
7277+ lsv -= elem_count - 1 ;
72217278 }
72227279
72237280 if (warn_ob_select) {
@@ -7229,18 +7286,27 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_do_(Design*des, NetScope*scope,
72297286 // Get the parameter values width.
72307287 long pwid = -1 ;
72317288 if (par_ex->has_width ()) pwid = par_ex->expr_width ()-1 ;
7232- warn_param_ob (par_msv, par_lsv, defined , lsv-par_base, wid,
7233- pwid, this , name, up);
7289+ warn_param_ob (par_msv, par_lsv, defined , lsv-par_base,
7290+ elem_count, pwid, this , name, up);
72347291 }
72357292
7293+ // Convert the element offset into a bit offset within the slice.
7294+ long base_bit = slice_off + (lsv-par_base) * slice_wid;
72367295 verinum result = param_part_select_bits (par_ex->value (), wid,
7237- lsv-par_base );
7296+ base_bit );
72387297 NetEConst*result_ex = new NetEConst (result);
72397298 result_ex->set_line (*this );
72407299 return result_ex;
72417300 }
72427301
7243- base = normalize_variable_base (base, par_msv, par_lsv, wid, up);
7302+ // Normalize the variable base to an element offset, then scale it to
7303+ // a bit offset. The prefix indices already moved the start of the
7304+ // slice by slice_off bits.
7305+ long slice_off_elems = slice_off / (long )slice_wid;
7306+ base = normalize_variable_base (base, par_msv, par_lsv, elem_count, up,
7307+ slice_off_elems);
7308+ if (slice_wid > 1 )
7309+ base = scale_expr_by_const (base, slice_wid);
72447310
72457311 /* Create a parameter reference for the variable select. */
72467312 NetEConstParam*ptmp = new NetEConstParam (found_in, name, par_ex->value ());
0 commit comments