Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial support for array assignment patterns #953

Merged
merged 5 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions PExpr.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,13 @@ class PEAssignPattern : public PExpr {
NetExpr* elaborate_expr_struct_(Design *des, NetScope *scope,
const netstruct_t *struct_type,
bool need_const) const;
NetExpr* elaborate_expr_darray_(Design *des, NetScope *scope,
const netdarray_t *array_type,
NetExpr* elaborate_expr_array_(Design *des, NetScope *scope,
const netarray_t *array_type,
bool need_const, bool up) const;
NetExpr* elaborate_expr_uarray_(Design *des, NetScope *scope,
const netuarray_t *uarray_type,
const std::vector<netrange_t> &dims,
unsigned int cur_dim,
bool need_const) const;

private:
Expand Down
97 changes: 90 additions & 7 deletions elab_expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type,
break;
}

// If the target is an unpacked array we want full type checking,
// regardless of the base type of the array.
if (dynamic_cast<const netuarray_t *>(lv_net_type))
typed_elab = true;

// Special case, PEAssignPattern is context dependend on the type and
// always uses the typed elaboration
if (dynamic_cast<PEAssignPattern*>(expr))
Expand Down Expand Up @@ -236,7 +241,13 @@ NetExpr*PEAssignPattern::elaborate_expr(Design*des, NetScope*scope,
bool need_const = NEED_CONST & flags;

if (auto darray_type = dynamic_cast<const netdarray_t*>(ntype))
return elaborate_expr_darray_(des, scope, darray_type, need_const);
return elaborate_expr_array_(des, scope, darray_type, need_const, true);

if (auto uarray_type = dynamic_cast<const netuarray_t*>(ntype)) {
return elaborate_expr_uarray_(des, scope, uarray_type,
uarray_type->static_dimensions(), 0,
need_const);
}

if (auto parray_type = dynamic_cast<const netparray_t*>(ntype)) {
return elaborate_expr_packed_(des, scope, parray_type->base_type(),
Expand Down Expand Up @@ -265,9 +276,9 @@ NetExpr*PEAssignPattern::elaborate_expr(Design*des, NetScope*scope,
return 0;
}

NetExpr* PEAssignPattern::elaborate_expr_darray_(Design *des, NetScope *scope,
const netdarray_t *array_type,
bool need_const) const
NetExpr* PEAssignPattern::elaborate_expr_array_(Design *des, NetScope *scope,
const netarray_t *array_type,
bool need_const, bool up) const
{
// Special case: If this is an empty pattern (i.e. '{}) then convert
// this to a null handle. Internally, Icarus Verilog uses this to
Expand All @@ -283,17 +294,89 @@ NetExpr* PEAssignPattern::elaborate_expr_darray_(Design *des, NetScope *scope,
// element_type expressions.
ivl_type_t elem_type = array_type->element_type();
vector<NetExpr*> elem_exprs (parms_.size());
size_t elem_idx = up ? 0 : parms_.size() - 1;
for (size_t idx = 0 ; idx < parms_.size() ; idx += 1) {
NetExpr*tmp = elaborate_rval_expr(des, scope, elem_type,
parms_[idx], need_const);
elem_exprs[idx] = tmp;
elem_exprs[elem_idx] = elaborate_rval_expr(des, scope, elem_type,
parms_[idx], need_const);
if (up)
elem_idx++;
else
elem_idx--;
}

NetEArrayPattern*res = new NetEArrayPattern(array_type, elem_exprs);
res->set_line(*this);
return res;
}

NetExpr* PEAssignPattern::elaborate_expr_uarray_(Design *des, NetScope *scope,
const netuarray_t *uarray_type,
const std::vector<netrange_t> &dims,
unsigned int cur_dim,
bool need_const) const
{
if (dims.size() <= cur_dim)
return nullptr;

if (dims[cur_dim].width() != parms_.size()) {
cerr << get_fileline() << ": error: Unpacked array assignment pattern expects "
<< dims[cur_dim].width() << " element(s) in this context.\n"
<< get_fileline() << ": : Found "
<< parms_.size() << " element(s)." << endl;
des->errors++;
}

bool up = dims[cur_dim].get_msb() < dims[cur_dim].get_lsb();
if (cur_dim == dims.size() - 1) {
return elaborate_expr_array_(des, scope, uarray_type, need_const, up);
}

cur_dim++;
vector<NetExpr*> elem_exprs(parms_.size());
size_t elem_idx = up ? 0 : parms_.size() - 1;
for (size_t idx = 0; idx < parms_.size(); idx++) {
NetExpr *expr = nullptr;
// Handle nested assignment patterns as a special case. We do not
// have a good way of passing the inner dimensions through the
// generic elaborate_expr() API and assigment patterns is the only
// place where we need it.
if (auto ap = dynamic_cast<PEAssignPattern*>(parms_[idx])) {
expr = ap->elaborate_expr_uarray_(des, scope, uarray_type,
dims, cur_dim, need_const);
} else if (dynamic_cast<PEConcat*>(parms_[idx])) {
cerr << get_fileline() << ": sorry: "
<< "Array concatenation is not yet supported."
<< endl;
des->errors++;
} else if (dynamic_cast<PEIdent*>(parms_[idx])) {
// The only other thing that's allow in this
// context is an array slice or identifier.
cerr << get_fileline() << ": sorry: "
<< "Procedural assignment of array or array slice"
<< " is not yet supported." << endl;
des->errors++;
} else if (parms_[idx]) {
cerr << get_fileline() << ": error: Expression "
<< *parms_[idx]
<< " is not compatible with this context."
<< " Expected array or array-like expression."
<< endl;
des->errors++;
}

elem_exprs[elem_idx] = expr;

if (up)
elem_idx++;
else
elem_idx--;
}

NetEArrayPattern *res = new NetEArrayPattern(uarray_type, elem_exprs);
res->set_line(*this);
return res;
}

NetExpr* PEAssignPattern::elaborate_expr_packed_(Design *des, NetScope *scope,
ivl_variable_type_t base_type,
unsigned int width,
Expand Down
8 changes: 6 additions & 2 deletions elab_lval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,12 @@ NetAssign_*PEIdent::elaborate_lval_var_(Design *des, NetScope *scope,
// is less than the array dimensions (unpacked).
if (reg->unpacked_dimensions() > name_tail.index.size()) {
if (gn_system_verilog()) {
cerr << get_fileline() << ": sorry: Assignment to an entire"
" array or to an array slice is not yet supported."
if (name_tail.index.empty()) {
NetAssign_*lv = new NetAssign_(reg);
return lv;
}
cerr << get_fileline() << ": sorry: Assignment to an "
" array slice is not yet supported."
<< endl;
} else {
cerr << get_fileline() << ": error: Assignment to an entire"
Expand Down
19 changes: 12 additions & 7 deletions elaborate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -251,25 +251,29 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
NetNet *elaborate_unpacked_array(Design *des, NetScope *scope, const LineInfo &loc,
const NetNet *lval, PExpr *expr)
{
NetNet *expr_net;
PEIdent* ident = dynamic_cast<PEIdent*> (expr);
if (!ident) {
des->errors++;
if (dynamic_cast<PEConcat*> (expr)) {
cout << loc.get_fileline() << ": sorry: Continuous assignment"
<< " of array concatenation is not yet supported."
<< endl;
des->errors++;
return nullptr;
} else if (dynamic_cast<PEAssignPattern*> (expr)) {
cout << loc.get_fileline() << ": sorry: Continuous assignment"
<< " of assignment pattern is not yet supported." << endl;
auto net_expr = elaborate_rval_expr(des, scope, lval->array_type(), expr);
expr_net = net_expr->synthesize(des, scope, net_expr);
} else {
cout << loc.get_fileline() << ": error: Can not assign"
<< " non-array expression `" << *expr << "` to array."
<< endl;
des->errors++;
return nullptr;
}
return nullptr;
} else {
expr_net = ident->elaborate_unpacked_net(des, scope);
}

NetNet *expr_net = ident->elaborate_unpacked_net(des, scope);
if (!expr_net)
return nullptr;

Expand Down Expand Up @@ -2642,8 +2646,9 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const
cerr << get_fileline() << ": PAssign::elaborate: "
<< "lv->word() = <nil>" << endl;
}
ivl_assert(*this, lv->word());
ivl_type_t use_lv_type = utype->element_type();
ivl_type_t use_lv_type = lv_net_type;
if (lv->word())
use_lv_type = utype->element_type();

ivl_assert(*this, use_lv_type);
rv = elaborate_rval_(des, scope, use_lv_type);
Expand Down
53 changes: 53 additions & 0 deletions expr_synth.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

# include "netlist.h"
# include "netvector.h"
# include "netparray.h"
# include "netmisc.h"
# include "ivl_assert.h"

Expand Down Expand Up @@ -813,6 +814,58 @@ NetNet* NetEConcat::synthesize(Design*des, NetScope*scope, NetExpr*root)
return osig;
}

NetNet *NetEArrayPattern::synthesize(Design *des, NetScope *scope, NetExpr *root)
{
const netsarray_t *array_type = dynamic_cast<const netsarray_t *>(net_type());
ivl_assert(*this, array_type);

if (items_.empty())
return nullptr;

bool failed = false;

std::unique_ptr<NetNet*[]> nets(new NetNet*[items_.size()]);
for (unsigned int idx = 0; idx < items_.size(); idx++) {
if (!items_[idx]) {
failed = true;
continue;
}
nets[idx] = items_[idx]->synthesize(des, scope, root);
if (!nets[idx])
failed = true;
}

if (failed)
return nullptr;

// Infer which dimension we are in for nested assignment patterns based on
// the dimensions of the element.
size_t dim = nets[0]->unpacked_dims().size() + 1;
const auto &type_dims = array_type->static_dimensions();

if (dim > type_dims.size())
return nullptr;

std::list<netrange_t> dims(type_dims.end() - dim, type_dims.end());

if (dims.front().width() != items_.size())
return nullptr;

perm_string path = scope->local_symbol();
NetNet *osig = new NetNet(scope, path, NetNet::IMPLICIT, dims,
array_type->element_type());
osig->set_line(*this);
osig->local_flag(true);

unsigned int opin = 0;
for (unsigned int idx = 0; idx < items_.size(); idx++) {
for (unsigned int net_pin = 0; net_pin < nets[idx]->pin_count(); net_pin++)
connect(osig->pin(opin++), nets[idx]->pin(net_pin));
}

return osig;
}

NetNet* NetEConst::synthesize(Design*des, NetScope*scope, NetExpr*)
{
perm_string path = scope->local_symbol();
Expand Down
44 changes: 44 additions & 0 deletions ivtest/ivltests/sv_ap_uarray1.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Check that procedural assignment of unpacked array assignment patterns is
// supported and a entries are assigned in the right order.

module test;

bit failed;

`define check(val, exp) do \
if (val !== exp) begin \
$display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \
failed = 1'b1; \
end \
while(0)

int x[3:0];
int y[0:3];
int z[4];

initial begin
x = '{1'b1, 1 + 1, 3.3, "TEST"};
y = '{1'b1, 1 + 1, 3.3, "TEST"};
z = '{1'b1, 1 + 1, 3.3, "TEST"};

`check(x[0], 1413829460);
`check(x[1], 3);
`check(x[2], 2);
`check(x[3], 1);

`check(y[0], 1);
`check(y[1], 2);
`check(y[2], 3);
`check(y[3], 1413829460);

`check(z[0], 1);
`check(z[1], 2);
`check(z[2], 3);
`check(z[3], 1413829460);

if (!failed) begin
$display("PASSED");
end
end

endmodule
45 changes: 45 additions & 0 deletions ivtest/ivltests/sv_ap_uarray2.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Check that procedural assignment of unpacked array assignment patterns to
// multi-dimensional arrays is supported and entries are assigned in the right
// order.

module test;

bit failed;

`define check(val, exp) do \
if (val !== exp) begin \
$display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \
failed = 1'b1; \
end \
while(0)

int x[1:0][1:0];
int y[1:0][0:1];
int z[2][2];

initial begin
x = '{'{1'b1, 1 + 1}, '{3.3, "TEST"}};
y = '{'{1'b1, 1 + 1}, '{3.3, "TEST"}};
z = '{'{1'b1, 1 + 1}, '{3.3, "TEST"}};

`check(x[0][0], 1413829460);
`check(x[0][1], 3);
`check(x[1][0], 2);
`check(x[1][1], 1);

`check(y[0][0], 3);
`check(y[0][1], 1413829460);
`check(y[1][0], 1);
`check(y[1][1], 2);

`check(z[0][0], 1);
`check(z[0][1], 2);
`check(z[1][0], 3);
`check(z[1][1], 1413829460);

if (!failed) begin
$display("PASSED");
end
end

endmodule
Loading