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:
parent
2d6243ea6c
commit
5ec72f4cc8
14
elab_net.cc
14
elab_net.cc
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
107
elaborate.cc
107
elaborate.cc
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
12
netparray.cc
12
netparray.cc
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
14
nettypes.cc
14
nettypes.cc
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue