Skip to content

Commit 879c653

Browse files
authored
Merge pull request #1671 from sifferman/packed-param-multi-select-merged
Fix selects of multi-dimension packed array parameters
2 parents 0accff4 + dfd3121 commit 879c653

9 files changed

Lines changed: 255 additions & 52 deletions

‎PExpr.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,8 @@ class PEIdent : public PExpr {
545545
unsigned long elem_w,
546546
bool packed_layout,
547547
NetExpr*sel,
548-
perm_string name) const;
548+
perm_string name,
549+
long slice_off =0) const;
549550
NetExpr*elaborate_expr_param_uarray_(Design*des,
550551
NetScope*scope,
551552
const NetEConst*par_ex,

‎elab_expr.cc‎

Lines changed: 117 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -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());
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Check that a part select of a packed array of a signed typedef is
2+
// unsigned and as wide as the elements it selects, while a select of one
3+
// element keeps the sign of the typedef.
4+
5+
module test;
6+
7+
typedef logic signed [3:0] elem_t;
8+
9+
elem_t [1:0] v;
10+
elem_t [1:0][1:0] g;
11+
12+
reg failed;
13+
reg [15:0] value;
14+
15+
`define check(val, exp) \
16+
if (val !== exp) begin \
17+
$display("FAILED(%0d). '%s' expected %h, got %h", `__LINE__, \
18+
`"val`", exp, val); \
19+
failed = 1'b1; \
20+
end
21+
22+
initial begin
23+
failed = 1'b0;
24+
v = 8'hee;
25+
g = 16'heeee;
26+
value = v[1:1];
27+
`check(value, 16'h000e);
28+
value = v[1 -: 1];
29+
`check(value, 16'h000e);
30+
value = g[1][1:1];
31+
`check(value, 16'h000e);
32+
value = v[1];
33+
`check(value, 16'hfffe);
34+
35+
if (!failed) begin
36+
$display("PASSED");
37+
end
38+
end
39+
40+
endmodule

0 commit comments

Comments
 (0)