Connect continuous array assignments in the right order

In SystemVerilog arrays assignments are supposed to be connected left to
right. This means if the left to right direction differs between the two
arrays they will be connected in reverse order.

E.g.
```
logic a[1:0];
logic b[0:1];
assign b = a;
```

should connect a[0] to b[1] and a[1] to b[0]. Things get a bit more tricky
for multi-dimensional arrays where some dimensions have a matching
direction and some do not.

The current implementation always connects them low to high, which results
in incorrect behavior.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2023-03-05 09:15:59 -08:00
parent b210eb8264
commit cc962310bb
1 changed files with 62 additions and 8 deletions

View File

@ -1628,21 +1628,75 @@ NetExpr*collapse_array_indices(Design*des, NetScope*scope, NetNet*net,
return res; return res;
} }
static void assign_unpacked_with_bufz_dim(Design *des, NetScope *scope,
const LineInfo *loc,
NetNet *lval, NetNet *rval,
const std::vector<long> &stride,
unsigned int dim = 0,
unsigned int idx_l = 0,
unsigned int idx_r = 0)
{
int inc_l, inc_r;
bool up_l, up_r;
const auto &l_dims = lval->unpacked_dims();
const auto &r_dims = rval->unpacked_dims();
up_l = l_dims[dim].get_msb() < l_dims[dim].get_lsb();
up_r = r_dims[dim].get_msb() < r_dims[dim].get_lsb();
inc_l = inc_r = stride[dim];
/*
* Arrays dimensions get connected left-to-right. This means if the
* left-to-right order differs for a particular dimension between the two
* arrays the elements for that dimension will get connected in reverse
* order.
*/
if (!up_l) {
/* Go to the last element and count down */
idx_l += inc_l * (l_dims[dim].width() - 1);
inc_l = -inc_l;
}
if (!up_r) {
/* Go to the last element and count down */
idx_r += inc_r * (r_dims[dim].width() - 1);
inc_r = -inc_r;
}
for (unsigned int idx = 0; idx < l_dims[dim].width(); idx++) {
if (dim == l_dims.size() - 1) {
NetBUFZ *driver = new NetBUFZ(scope, scope->local_symbol(),
lval->vector_width(), false);
driver->set_line(*loc);
des->add_node(driver);
connect(lval->pin(idx_l), driver->pin(0));
connect(driver->pin(1), rval->pin(idx_r));
} else {
assign_unpacked_with_bufz_dim(des, scope, loc, lval, rval,
stride, dim + 1, idx_l, idx_r);
}
idx_l += inc_l;
idx_r += inc_r;
}
}
void assign_unpacked_with_bufz(Design*des, NetScope*scope, void assign_unpacked_with_bufz(Design*des, NetScope*scope,
const LineInfo*loc, const LineInfo*loc,
NetNet*lval, NetNet*rval) NetNet*lval, NetNet*rval)
{ {
ivl_assert(*loc, lval->pin_count()==rval->pin_count()); ivl_assert(*loc, lval->pin_count()==rval->pin_count());
for (unsigned idx = 0 ; idx < lval->pin_count() ; idx += 1) { const auto &dims = lval->unpacked_dims();
NetBUFZ*driver = new NetBUFZ(scope, scope->local_symbol(), vector<long> stride(dims.size());
lval->vector_width(), false);
driver->set_line(*loc);
des->add_node(driver);
connect(lval->pin(idx), driver->pin(0)); make_strides(dims, stride);
connect(driver->pin(1), rval->pin(idx)); assign_unpacked_with_bufz_dim(des, scope, loc, lval, rval, stride);
}
} }
/* /*