Allow nets & variables to be elaborated early on demand.

If a net or variable is referenced in another net or variable declaration
or in a value parameter definition (e.g. when using the $bits function)
and hasn't already been elaborated, we need to elaborate it early. So
during the scope elaboration phase, add placeholders in each NetScope
object to record the PWire objects that are yet to be elaborated. This
allows the symbol_search() function to find the unelaborated objects
and to trigger early elaboration.

Add a flag in the PWire object to indicate when we are elaborating it.
This allows us to detect circular references and avoid an infinite loop.

This fixes issue #483, issue #575, and issue #1097.
This commit is contained in:
Martin Whitaker 2024-02-12 18:38:00 +00:00
parent ff4cd2c5da
commit ca307053f2
6 changed files with 99 additions and 2 deletions

View File

@ -91,7 +91,7 @@ class PWire : public PNamedItem {
// Write myself to the specified stream.
void dump(std::ostream&out, unsigned ind=4) const;
NetNet* elaborate_sig(Design*, NetScope*scope) const;
NetNet* elaborate_sig(Design*, NetScope*scope);
SymbolType symbol_type() const;
@ -110,6 +110,8 @@ class PWire : public PNamedItem {
// Whether the wire is variable declared with the const keyword.
bool is_const_ = false;
bool is_elaborating_ = false;
// These members hold expressions for the bit width of the
// wire. If they do not exist, the wire is 1 bit wide. If they
// do exist, they represent the packed dimensions of the

View File

@ -160,6 +160,22 @@ static void collect_scope_specparams(Design*des, NetScope*scope,
}
}
static void collect_scope_signals(NetScope*scope,
const map<perm_string,PWire*>&wires)
{
for (map<perm_string,PWire*>::const_iterator cur = wires.begin()
; cur != wires.end() ; ++ cur ) {
PWire*wire = (*cur).second;
if (debug_scopes) {
cerr << wire->get_fileline() << ": " << __func__ << ": "
<< "adding placeholder for signal '" << wire->basename()
<< "' in scope '" << scope_path(scope) << "'." << endl;
}
scope->add_signal_placeholder(wire);
}
}
/*
* Elaborate the enumeration into the given scope.
*/
@ -498,6 +514,8 @@ static void elaborate_scope_class(Design*des, NetScope*scope, PClass*pclass)
collect_scope_parameters(des, class_scope, pclass->parameters);
collect_scope_signals(class_scope, pclass->wires);
// Elaborate enum types declared in the class. We need these
// now because enumeration constants can be used during scope
// elaboration.
@ -725,6 +743,8 @@ bool PPackage::elaborate_scope(Design*des, NetScope*scope)
collect_scope_parameters(des, scope, parameters);
collect_scope_signals(scope, wires);
if (debug_scopes) {
cerr << get_fileline() << ": PPackage::elaborate_scope: "
<< "Elaborate " << enum_sets.size() << " enumerations"
@ -765,6 +785,8 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
collect_scope_specparams(des, scope, specparams);
collect_scope_signals(scope, wires);
// Run parameter replacements that were collected from the
// containing scope and meant for me.
@ -1239,6 +1261,8 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope)
// module have been done.
collect_scope_parameters(des, scope, parameters);
collect_scope_signals(scope, wires);
// Run through the defparams for this scope and save the result
// in a table for later final override.
@ -1577,6 +1601,8 @@ void PFunction::elaborate_scope(Design*des, NetScope*scope) const
collect_scope_parameters(des, scope, parameters);
collect_scope_signals(scope, wires);
// Scan through all the named events in this scope.
elaborate_scope_events_(des, scope, events);
@ -1595,6 +1621,8 @@ void PTask::elaborate_scope(Design*des, NetScope*scope) const
collect_scope_parameters(des, scope, parameters);
collect_scope_signals(scope, wires);
// Scan through all the named events in this scope.
elaborate_scope_events_(des, scope, events);
@ -1643,6 +1671,8 @@ void PBlock::elaborate_scope(Design*des, NetScope*scope) const
collect_scope_parameters(des, my_scope, parameters);
collect_scope_signals(my_scope, wires);
// Scan through all the named events in this scope.
elaborate_scope_events_(des, my_scope, events);
}

View File

@ -1011,13 +1011,27 @@ ivl_type_t PWire::elaborate_type(Design*des, NetScope*scope,
* elaboration this creates an object in the design that represents the
* defined item.
*/
NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
NetNet* PWire::elaborate_sig(Design*des, NetScope*scope)
{
// This sets the vector or array dimension size that will
// cause a warning. For now, these warnings are permanently
// enabled.
const long warn_dimension_size = 1 << 30;
// Check if we elaborated this signal earlier because it was
// used in another declaration.
if (NetNet*sig = scope->find_signal(name_))
return sig;
if (is_elaborating_) {
cerr << get_fileline() << ": error: Circular dependency "
"detected in declaration of '" << name_ << "'."
<< endl;
des->errors += 1;
return 0;
}
is_elaborating_ = true;
NetNet::Type wtype = type_;
if (wtype == NetNet::IMPLICIT)
wtype = NetNet::WIRE;
@ -1223,5 +1237,8 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
sig->set_const(is_const_);
scope->rem_signal_placeholder(this);
is_elaborating_ = false;
return sig;
}

View File

@ -27,6 +27,7 @@
# include "netvector.h"
# include "PExpr.h"
# include "PPackage.h"
# include "PWire.h"
# include <cstring>
# include <cstdlib>
# include <sstream>
@ -702,6 +703,24 @@ LineInfo* NetScope::find_genvar(perm_string name)
return 0;
}
void NetScope::add_signal_placeholder(PWire*wire)
{
signal_placeholders_[wire->basename()] = wire;
}
void NetScope::rem_signal_placeholder(PWire*wire)
{
signal_placeholders_.erase(wire->basename());
}
PWire* NetScope::find_signal_placeholder(perm_string name)
{
if (signal_placeholders_.find(name) != signal_placeholders_.end())
return signal_placeholders_[name];
else
return 0;
}
void NetScope::add_signal(NetNet*net)
{
signals_map_[net->name()]=net;

View File

@ -81,6 +81,7 @@ class PExpr;
class PFunction;
class PPackage;
class PTaskFunc;
class PWire;
class data_type_t;
struct enum_type_t;
class netclass_t;
@ -1013,6 +1014,15 @@ class NetScope : public Definitions, public Attrib {
void add_genvar(perm_string name, LineInfo *li);
LineInfo* find_genvar(perm_string name);
/* These methods manage unelaborated signals. These are added to
the scope as placeholders during the scope elaboration phase,
to allow signal declarations to refer to other signals (e.g.
when using $bits in a range definition), regardless of the
order in which the signals are elaborated. */
void add_signal_placeholder(PWire*);
void rem_signal_placeholder(PWire*);
PWire* find_signal_placeholder(perm_string name);
/* These methods manage signals. The add_ and rem_signal
methods are used by the NetNet objects to make themselves
available to the scope, and the find_signal method can be
@ -1314,6 +1324,8 @@ class NetScope : public Definitions, public Attrib {
std::map<perm_string,LineInfo*> genvars_;
std::map<perm_string,PWire*> signal_placeholders_;
typedef std::map<perm_string,NetNet*>::const_iterator signals_map_iter_t;
std::map <perm_string,NetNet*> signals_map_;
perm_string module_name_;

View File

@ -24,6 +24,7 @@
# include "netmisc.h"
# include "compiler.h"
# include "PPackage.h"
# include "PWire.h"
# include "ivl_assert.h"
using namespace std;
@ -219,6 +220,22 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope,
return true;
}
}
// Finally check the rare case of a signal that hasn't
// been elaborated yet.
if (PWire*wire = scope->find_signal_placeholder(path_tail.name)) {
if (prefix_scope || (wire->lexical_pos() <= lexical_pos)) {
NetNet*net = wire->elaborate_sig(des, scope);
if (!net)
return false;
path.push_back(path_tail);
res->scope = scope;
res->net = net;
res->type = net->net_type();
res->path_head = path;
return true;
}
}
}
// Could not find an object. Maybe this is a child scope name? If