Add error checking for continuous unpacked array assignments

Currently there is no error checking for continuous assignments to unpacked
arrays. If the lvalue and rvalue net are not compatible undefined behavior
occurs. For some types of incompatibility it will crash during elaboration,
for others it will crash during simulation, and for some it will just work,
even though the assignment is not allowed by the standard.

Implement checking to ensure the two nets are compatible as required by the
standard and report an error otherwise.

Two arrays are considered to be compatible if their element types are
equivalent, they have the same number of ranges and each range has the same
number of elements.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2022-09-12 22:13:25 +02:00
parent 2d6243ea6c
commit 5ec72f4cc8
6 changed files with 131 additions and 25 deletions

View File

@ -1107,8 +1107,20 @@ NetNet*PEIdent::elaborate_unpacked_net(Design*des, NetScope*scope) const
perm_string method_name;
symbol_search(this, des, scope, path_, sig, par, eve);
if (!sig) {
cerr << get_fileline() << ": error: Net " << path_
<< " is not defined in this context." << endl;
des->errors += 1;
return nullptr;
}
ivl_assert(*this, sig);
const name_component_t&name_tail = path_.back();
if (name_tail.index.size() != 0) {
cerr << get_fileline() << ": sorry: Array slices are not yet "
<< "supported for continuous assignment." << endl;
des->errors += 1;
return nullptr;
}
return sig;
}

View File

@ -260,16 +260,64 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
}
NetNet *elaborate_unpacked_array(Design *des, NetScope *scope, const LineInfo &loc,
const NetNet *lval, PExpr *expr)
{
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;
} else if (dynamic_cast<PEAssignPattern*> (expr)) {
cout << loc.get_fileline() << ": sorry: Continuous assignment"
<< " of assignment pattern is not yet supported." << endl;
} else {
cout << loc.get_fileline() << ": error: Can not assign"
<< " non-array expression `" << *expr << "` to array."
<< endl;
}
return nullptr;
}
NetNet *expr_net = ident->elaborate_unpacked_net(des, scope);
if (!expr_net)
return nullptr;
auto const &lval_dims = lval->unpacked_dims();
auto const &expr_dims = expr_net->unpacked_dims();
if (expr_dims.empty()) {
cerr << loc.get_fileline() << ": error: Can not assign"
<< " non-array identifier `" << *expr << "` to array."
<< endl;
des->errors++;
return nullptr;
}
if (!netrange_equivalent(lval_dims, expr_dims)) {
cerr << loc.get_fileline() << ": error: Unpacked dimensions"
<< " are not compatible in array assignment." << endl;
des->errors++;
return nullptr;
}
if (!lval->net_type()->type_equivalent(expr_net->net_type())) {
cerr << loc.get_fileline() << ": error: Element types are not"
<< " compatible in array assignment." << endl;
des->errors++;
return nullptr;
}
return expr_net;
}
void PGAssign::elaborate_unpacked_array_(Design*des, NetScope*scope, NetNet*lval) const
{
PEIdent*rval_pident = dynamic_cast<PEIdent*> (pin(1));
ivl_assert(*this, rval_pident);
NetNet*rval_net = rval_pident->elaborate_unpacked_net(des, scope);
ivl_assert(*this, rval_net->pin_count() == lval->pin_count());
assign_unpacked_with_bufz(des, scope, this, lval, rval_net);
NetNet *rval_net = elaborate_unpacked_array(des, scope, *this, lval, pin(1));
if (rval_net)
assign_unpacked_with_bufz(des, scope, lval, lval, rval_net);
}
void PGBuiltin::calculate_gate_and_lval_count_(unsigned&gate_count,
@ -1147,6 +1195,29 @@ static void isolate_and_connect(Design*des, NetScope*scope, const PGModule*mod,
}
}
void elaborate_unpacked_port(Design *des, NetScope *scope, NetNet *port_net,
PExpr *expr, NetNet::PortType port_type,
Module *mod, unsigned int port_idx)
{
NetNet *expr_net = elaborate_unpacked_array(des, scope, *expr, port_net,
expr);
if (!expr_net) {
perm_string port_name = mod->get_port_name(port_idx);
cerr << expr->get_fileline() << ": : Port "
<< port_idx+1 << " (" << port_name << ") of "
<< mod->mod_name() << " is connected to "
<< *expr << endl;
return;
}
ivl_assert(*port_net, expr_net->pin_count() == port_net->pin_count());
if (port_type == NetNet::POUTPUT)
assign_unpacked_with_bufz(des, scope, port_net, expr_net, port_net);
else
assign_unpacked_with_bufz(des, scope, port_net, port_net, expr_net);
}
/*
* Instantiate a module by recursively elaborating it. Set the path of
* the recursive elaboration so that signal names get properly
@ -1481,13 +1552,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
// differently.
if (prts.size() >= 1 && prts[0]->pin_count()>1) {
ivl_assert(*this, prts.size()==1);
PEIdent*rval_pident = dynamic_cast<PEIdent*> (pins[idx]);
ivl_assert(*this, rval_pident);
NetNet*rval_net = rval_pident->elaborate_unpacked_net(des, scope);
ivl_assert(*this, rval_net->pin_count() == prts[0]->pin_count());
assign_unpacked_with_bufz(des, scope, this, prts[0], rval_net);
elaborate_unpacked_port(des, scope, prts[0], pins[idx],
ptype, rmod, idx);
continue;
}
@ -1650,15 +1716,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
// "r-value" expression, but since this is an
// output port, we assign to it from the internal object.
if (prts[0]->pin_count() > 1) {
ivl_assert(*this, prts.size()==1);
PEIdent*rval_pident = dynamic_cast<PEIdent*>(pins[idx]);
ivl_assert(*this, rval_pident);
NetNet*rval_net = rval_pident->elaborate_unpacked_net(des, scope);
ivl_assert(*this, rval_net->pin_count() == prts[0]->pin_count());
assign_unpacked_with_bufz(des, scope, this, rval_net, prts[0]);
elaborate_unpacked_port(des, scope, prts[0], pins[idx],
ptype, rmod, idx);
continue;
}

View File

@ -86,3 +86,15 @@ vector<netrange_t> netuarray_t::slice_dimensions() const
{
return static_dimensions();
}
bool netuarray_t::test_equivalence(ivl_type_t that) const
{
const netuarray_t *that_a = dynamic_cast<const netuarray_t *>(that);
if (!that_a)
return false;
if (!netrange_equivalent(static_dimensions(), that_a->static_dimensions()))
return false;
return element_type()->type_equivalent(that_a->element_type());
}

View File

@ -92,6 +92,9 @@ class netuarray_t : public netsarray_t {
public:
// Virtual methods from the ivl_type_s type...
std::vector<netrange_t> slice_dimensions() const;
private:
bool test_equivalence(ivl_type_t that) const;
};
inline netuarray_t::netuarray_t(const std::vector<netrange_t>&pd,

View File

@ -109,6 +109,20 @@ unsigned long netrange_width(const vector<netrange_t>&packed)
return wid;
}
bool netrange_equivalent(const std::vector<netrange_t> &a,
const std::vector<netrange_t> &b)
{
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); i++) {
if (!a[i].equivalent(b[i]))
return false;
}
return true;
}
/*
* Given a netrange_t list (which represent packed dimensions) and a
* prefix of calculated index values, calculate the canonical offset

View File

@ -137,6 +137,10 @@ class netrange_t {
return false;
}
bool equivalent(const netrange_t &that) const {
return width() == that.width();
}
private:
long msb_;
long lsb_;
@ -146,6 +150,8 @@ extern std::ostream&operator << (std::ostream&out, const std::list<netrange_t>&r
extern std::ostream&operator << (std::ostream&out, const std::vector<netrange_t>&rlist);
extern unsigned long netrange_width(const std::vector<netrange_t>&dims);
extern bool netrange_equivalent(const std::vector<netrange_t> &a,
const std::vector<netrange_t> &b);
/*
* There are a few cases where we need to know about the single-level