From 89bb86962fab1ac71cab974aea399043d27813eb Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sat, 3 Feb 2024 17:55:32 +0000 Subject: [PATCH] Allow mixed procedural/continuous assignment for array words. This is legal if the procedural and continuous assignments target different words. NOTE: This is not fully compliant with the standard, because vvp does not know that the nets were originally declared as variables, so initialises to 'bz instead of 'bx and does not handle release correctly. --- elab_lval.cc | 14 +++++++++----- netlist.cc | 24 ++++++++++++++++++++++++ netlist.h | 7 +++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/elab_lval.cc b/elab_lval.cc index 2c2308081..fd3058a12 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -468,11 +468,15 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, if ((reg->type()==NetNet::UNRESOLVED_WIRE) && !is_force) { ivl_assert(*this, reg->coerced_to_uwire()); - cerr << get_fileline() << ": error: Cannot perform " - "procedural assignment to word in array '" << reg->name() - << "' because it is also continuously assigned." << endl; - des->errors += 1; - return 0; + NetEConst*canon_const = dynamic_cast(canon_index); + if (!canon_const || reg->test_part_driven(reg->vector_width() - 1, 0, + canon_const->value().as_long())) { + cerr << get_fileline() << ": error: Cannot perform " + "procedural assignment to word in array '" << reg->name() + << "' because it is also continuously assigned." << endl; + des->errors += 1; + return 0; + } } NetAssign_*lv = new NetAssign_(reg); diff --git a/netlist.cc b/netlist.cc index 716044101..0ad54660f 100644 --- a/netlist.cc +++ b/netlist.cc @@ -829,6 +829,30 @@ unsigned NetNet::peek_eref() const return eref_count_; } +/* + * Test each of the bits in the range. If any bits are set then return true. + */ +bool NetNet::test_part_driven(unsigned pmsb, unsigned plsb, int widx) +{ + if (lref_mask_.empty()) + return false; + + // If indexing a word that doesn't exist, then pretend this is + // never driven. + if (widx < 0) + return false; + if (widx >= (int)pin_count()) + return false; + + unsigned word_base = vector_width() * widx; + for (unsigned idx = plsb ; idx <= pmsb ; idx += 1) { + if (lref_mask_[idx+word_base]) + return true; + } + + return false; +} + /* * Test each of the bits in the range, and set them. If any bits are * already set then return true. diff --git a/netlist.h b/netlist.h index 06f12fcb5..68a408899 100644 --- a/netlist.h +++ b/netlist.h @@ -793,6 +793,13 @@ class NetNet : public NetObj, public PortType { // only useful for UNRESOLVED_WIRE objects. The msb and lsb // are the part select of the signal, and the widx is the word // index if this is an unpacked array. + bool test_part_driven(unsigned msb, unsigned lsb, int widx =0); + + // Treating this node as a uwire, this function tests whether + // any bits in the canonical part are already driven and sets + // them if not. This is only useful for UNRESOLVED_WIRE objects. + // The msb and lsb are the part select of the signal, and the + // widx is the word index if this is an unpacked array. bool test_and_set_part_driver(unsigned msb, unsigned lsb, int widx =0); unsigned get_refs() const;