Restructure Nexus lists of Links to handle large net lists.
When netlists get very large, the Nexus::connect() method tickles the O(N) performance and elaboration gets very slow. Rework the connect method to be O(C), for a drastic performance boost for large designs.
This commit is contained in:
parent
4db4a467ee
commit
782c55f7b5
|
|
@ -34,7 +34,7 @@ bool Nexus::drivers_constant() const
|
||||||
if (driven_ != NO_GUESS)
|
if (driven_ != NO_GUESS)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (const Link*cur = list_ ; cur ; cur = cur->next_) {
|
for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||||
const NetNet*sig;
|
const NetNet*sig;
|
||||||
Link::DIR cur_dir;
|
Link::DIR cur_dir;
|
||||||
|
|
||||||
|
|
@ -121,7 +121,7 @@ verinum::V Nexus::driven_value() const
|
||||||
|
|
||||||
verinum::V val = verinum::Vz;
|
verinum::V val = verinum::Vz;
|
||||||
|
|
||||||
for (cur = list_ ; cur ; cur = cur->next_) {
|
for (cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||||
|
|
||||||
const NetConst*obj;
|
const NetConst*obj;
|
||||||
const NetNet*sig;
|
const NetNet*sig;
|
||||||
|
|
|
||||||
222
net_link.cc
222
net_link.cc
|
|
@ -32,7 +32,8 @@
|
||||||
|
|
||||||
void Nexus::connect(Link&r)
|
void Nexus::connect(Link&r)
|
||||||
{
|
{
|
||||||
if (this == r.nexus_)
|
Nexus*r_nexus = r.next_? r.find_nexus_() : 0;
|
||||||
|
if (this == r_nexus)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (name_) {
|
if (name_) {
|
||||||
|
|
@ -40,31 +41,54 @@ void Nexus::connect(Link&r)
|
||||||
name_ = 0;
|
name_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case: The "r" link is connected to nothing. The
|
// Special case: This nexus is empty. Simply copy all the
|
||||||
// connect becomes trivially easy.
|
// links of the other nexus to this one, and delete the old
|
||||||
if (r.nexus_ == 0) {
|
// nexus.
|
||||||
|
if (list_ == 0) {
|
||||||
|
if (r.next_ == 0) {
|
||||||
|
list_ = &r;
|
||||||
|
r.next_ = &r;
|
||||||
|
r.nexus_ = this;
|
||||||
|
driven_ = NO_GUESS;
|
||||||
|
} else {
|
||||||
|
driven_ = r_nexus->driven_;
|
||||||
|
list_ = r_nexus->list_;
|
||||||
|
list_->nexus_ = this;
|
||||||
|
r_nexus->list_ = 0;
|
||||||
|
delete r_nexus;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special case: The Link is unconnected. Put it at the end of
|
||||||
|
// the current list and move the list_ pointer and nexus_ back
|
||||||
|
// pointer to suit.
|
||||||
|
if (r.next_ == 0) {
|
||||||
if (r.get_dir() != Link::INPUT)
|
if (r.get_dir() != Link::INPUT)
|
||||||
driven_ = NO_GUESS;
|
driven_ = NO_GUESS;
|
||||||
|
|
||||||
r.nexus_ = this;
|
r.nexus_ = this;
|
||||||
r.next_ = list_;
|
r.next_ = list_->next_;
|
||||||
|
list_->next_ = &r;
|
||||||
|
list_->nexus_ = 0;
|
||||||
list_ = &r;
|
list_ = &r;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Nexus*tmp = r.nexus_;
|
if (r_nexus->driven_ != Vz)
|
||||||
if (tmp->driven_ != Vz)
|
|
||||||
driven_ = NO_GUESS;
|
driven_ = NO_GUESS;
|
||||||
|
|
||||||
while (Link*cur = tmp->list_) {
|
// Splice the list of links from the "tmp" nexus to the end of
|
||||||
tmp->list_ = cur->next_;
|
// this nexus. Adjust the nexus pointers as needed.
|
||||||
cur->nexus_ = this;
|
Link*save_first = list_->next_;
|
||||||
cur->next_ = list_;
|
list_->next_ = r_nexus->list_->next_;
|
||||||
list_ = cur;
|
r_nexus->list_->next_ = save_first;
|
||||||
}
|
list_->nexus_ = 0;
|
||||||
|
list_ = r_nexus->list_;
|
||||||
|
list_->nexus_ = this;
|
||||||
|
|
||||||
assert(tmp->list_ == 0);
|
r_nexus->list_ = 0;
|
||||||
delete tmp;
|
delete r_nexus;
|
||||||
}
|
}
|
||||||
|
|
||||||
void connect(Link&l, Link&r)
|
void connect(Link&l, Link&r)
|
||||||
|
|
@ -75,8 +99,7 @@ void connect(Link&l, Link&r)
|
||||||
} else if (r.nexus_ != 0) {
|
} else if (r.nexus_ != 0) {
|
||||||
connect(r.nexus_, l);
|
connect(r.nexus_, l);
|
||||||
} else {
|
} else {
|
||||||
Nexus*tmp = new Nexus;
|
Nexus*tmp = new Nexus(l);
|
||||||
tmp->connect(l);
|
|
||||||
tmp->connect(r);
|
tmp->connect(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,25 +112,39 @@ Link::Link()
|
||||||
|
|
||||||
Link::~Link()
|
Link::~Link()
|
||||||
{
|
{
|
||||||
if (Nexus*tmp = nexus_) {
|
if (next_) {
|
||||||
nexus_->unlink(this);
|
Nexus*tmp = nexus();
|
||||||
|
tmp->unlink(this);
|
||||||
if (tmp->list_ == 0)
|
if (tmp->list_ == 0)
|
||||||
delete tmp;
|
delete tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Nexus* Link::find_nexus_() const
|
||||||
|
{
|
||||||
|
assert(next_);
|
||||||
|
if (nexus_) return nexus_;
|
||||||
|
for (Link*cur = next_ ; cur != this ; cur = cur->next_) {
|
||||||
|
if (cur->nexus_) return cur->nexus_;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Nexus* Link::nexus()
|
Nexus* Link::nexus()
|
||||||
{
|
{
|
||||||
if (nexus_ == 0) {
|
if (next_ == 0) {
|
||||||
Nexus*tmp = new Nexus;
|
assert(nexus_ == 0);
|
||||||
tmp->relink_(this);
|
Nexus*tmp = new Nexus(*this);
|
||||||
|
return tmp;
|
||||||
}
|
}
|
||||||
return nexus_;
|
|
||||||
|
return find_nexus_();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Nexus* Link::nexus() const
|
const Nexus* Link::nexus() const
|
||||||
{
|
{
|
||||||
return nexus_;
|
if (next_ == 0) return 0;
|
||||||
|
return find_nexus_();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Link::set_dir(DIR d)
|
void Link::set_dir(DIR d)
|
||||||
|
|
@ -122,12 +159,12 @@ Link::DIR Link::get_dir() const
|
||||||
|
|
||||||
void Link::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
|
void Link::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
|
||||||
{
|
{
|
||||||
nexus_->drivers_delays(rise, fall, decay);
|
find_nexus_()->drivers_delays(rise, fall, decay);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Link::drivers_drive(strength_t drive0__, strength_t drive1__)
|
void Link::drivers_drive(strength_t drive0__, strength_t drive1__)
|
||||||
{
|
{
|
||||||
nexus_->drivers_drive(drive0__, drive1__);
|
find_nexus_()->drivers_drive(drive0__, drive1__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Link::drive0(Link::strength_t str)
|
void Link::drive0(Link::strength_t str)
|
||||||
|
|
@ -175,11 +212,10 @@ void Link::cur_link(const NetPins*&net, unsigned &pin) const
|
||||||
|
|
||||||
void Link::unlink()
|
void Link::unlink()
|
||||||
{
|
{
|
||||||
assert(nexus_);
|
|
||||||
if (! is_linked())
|
if (! is_linked())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nexus_->unlink(this);
|
find_nexus_()->unlink(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Link::is_equal(const Link&that) const
|
bool Link::is_equal(const Link&that) const
|
||||||
|
|
@ -189,31 +225,44 @@ bool Link::is_equal(const Link&that) const
|
||||||
|
|
||||||
bool Link::is_linked() const
|
bool Link::is_linked() const
|
||||||
{
|
{
|
||||||
if (nexus_ == 0)
|
if (next_ == 0)
|
||||||
|
return false;
|
||||||
|
if (next_ == this)
|
||||||
return false;
|
return false;
|
||||||
if (next_)
|
|
||||||
return true;
|
|
||||||
if (nexus_->first_nlink() != this)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Link::is_linked(const Link&that) const
|
bool Link::is_linked(const Link&that) const
|
||||||
{
|
{
|
||||||
if (nexus_ == 0)
|
if (next_ == 0)
|
||||||
return false;
|
return false;
|
||||||
return nexus_ == that.nexus_;
|
if (that.next_ == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const Link*cur = next_;
|
||||||
|
while (cur != this) {
|
||||||
|
if (cur == &that) return true;
|
||||||
|
cur = cur->next_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this link has a nexus_ pointer, then it is the last Link in the
|
||||||
|
* list. next_nlink() returns 0 for the last Link.
|
||||||
|
*/
|
||||||
Link* Link::next_nlink()
|
Link* Link::next_nlink()
|
||||||
{
|
{
|
||||||
return next_;
|
if (nexus_) return 0;
|
||||||
|
else return next_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Link* Link::next_nlink() const
|
const Link* Link::next_nlink() const
|
||||||
{
|
{
|
||||||
return next_;
|
if (nexus_) return 0;
|
||||||
|
else return next_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NetPins*Link::get_obj() const
|
const NetPins*Link::get_obj() const
|
||||||
|
|
@ -242,12 +291,29 @@ unsigned Link::get_pin() const
|
||||||
return pin_;
|
return pin_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Nexus::Nexus()
|
Nexus::Nexus(Link&that)
|
||||||
{
|
{
|
||||||
name_ = 0;
|
name_ = 0;
|
||||||
list_ = 0;
|
|
||||||
driven_ = NO_GUESS;
|
driven_ = NO_GUESS;
|
||||||
t_cookie_ = 0;
|
t_cookie_ = 0;
|
||||||
|
|
||||||
|
if (that.next_ == 0) {
|
||||||
|
list_ = &that;
|
||||||
|
that.next_ = &that;
|
||||||
|
that.nexus_ = this;
|
||||||
|
driven_ = NO_GUESS;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Nexus*tmp = that.find_nexus_();
|
||||||
|
list_ = tmp->list_;
|
||||||
|
list_->nexus_ = this;
|
||||||
|
driven_ = tmp->driven_;
|
||||||
|
name_ = tmp->name_;
|
||||||
|
|
||||||
|
tmp->list_ = 0;
|
||||||
|
tmp->name_ = 0;
|
||||||
|
delete tmp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Nexus::~Nexus()
|
Nexus::~Nexus()
|
||||||
|
|
@ -259,8 +325,7 @@ Nexus::~Nexus()
|
||||||
|
|
||||||
verinum::V Nexus::get_init() const
|
verinum::V Nexus::get_init() const
|
||||||
{
|
{
|
||||||
assert(list_);
|
for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||||
for (Link*cur = list_ ; cur ; cur = cur->next_) {
|
|
||||||
if (cur->get_dir() == Link::OUTPUT)
|
if (cur->get_dir() == Link::OUTPUT)
|
||||||
return verinum::Vx;
|
return verinum::Vx;
|
||||||
|
|
||||||
|
|
@ -274,8 +339,7 @@ verinum::V Nexus::get_init() const
|
||||||
|
|
||||||
bool Nexus::assign_lval() const
|
bool Nexus::assign_lval() const
|
||||||
{
|
{
|
||||||
assert(list_);
|
for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||||
for (Link*cur = list_ ; cur ; cur = cur->next_) {
|
|
||||||
|
|
||||||
const NetPins*obj;
|
const NetPins*obj;
|
||||||
unsigned pin;
|
unsigned pin;
|
||||||
|
|
@ -293,8 +357,7 @@ bool Nexus::assign_lval() const
|
||||||
|
|
||||||
bool Nexus::drivers_present() const
|
bool Nexus::drivers_present() const
|
||||||
{
|
{
|
||||||
assert(list_);
|
for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||||
for (Link*cur = list_ ; cur ; cur = cur->next_) {
|
|
||||||
if (cur->get_dir() == Link::OUTPUT)
|
if (cur->get_dir() == Link::OUTPUT)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
@ -328,7 +391,7 @@ bool Nexus::drivers_present() const
|
||||||
|
|
||||||
void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
|
void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
|
||||||
{
|
{
|
||||||
for (Link*cur = list_ ; cur ; cur = cur->next_) {
|
for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||||
if (cur->get_dir() != Link::OUTPUT)
|
if (cur->get_dir() != Link::OUTPUT)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -344,7 +407,7 @@ void Nexus::drivers_delays(NetExpr*rise, NetExpr*fall, NetExpr*decay)
|
||||||
|
|
||||||
void Nexus::drivers_drive(Link::strength_t drive0, Link::strength_t drive1)
|
void Nexus::drivers_drive(Link::strength_t drive0, Link::strength_t drive1)
|
||||||
{
|
{
|
||||||
for (Link*cur = list_ ; cur ; cur = cur->next_) {
|
for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||||
if (cur->get_dir() != Link::OUTPUT)
|
if (cur->get_dir() != Link::OUTPUT)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -360,52 +423,57 @@ void Nexus::unlink(Link*that)
|
||||||
name_ = 0;
|
name_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the link I'm removing was a driver for this nexus, then
|
|
||||||
cancel my guess of the driven value. */
|
|
||||||
if (that->get_dir() != Link::INPUT)
|
|
||||||
driven_ = NO_GUESS;
|
|
||||||
|
|
||||||
assert(that);
|
assert(that);
|
||||||
if (list_ == that) {
|
|
||||||
list_ = that->next_;
|
// Special case: the Link is the only link in the nexus. In
|
||||||
that->next_ = 0;
|
// this case, the unlink is trivial. Also clear the Nexus
|
||||||
|
// pointers.
|
||||||
|
if (that->next_ == that) {
|
||||||
|
assert(that->nexus_ == this);
|
||||||
|
assert(list_ == that);
|
||||||
|
list_ = 0;
|
||||||
|
driven_ = NO_GUESS;
|
||||||
that->nexus_ = 0;
|
that->nexus_ = 0;
|
||||||
|
that->next_ = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Link*cur = list_;
|
// If the link I'm removing was a driver for this nexus, then
|
||||||
while (cur->next_ != that) {
|
// cancel my guess of the driven value.
|
||||||
assert(cur->next_);
|
if (that->get_dir() != Link::INPUT)
|
||||||
cur = cur->next_;
|
driven_ = NO_GUESS;
|
||||||
|
|
||||||
|
// Look for the Link that points to "that". We know that there
|
||||||
|
// will be one because the list is a circle. When we find the
|
||||||
|
// prev pointer, then remove that from the list.
|
||||||
|
Link*prev = list_;
|
||||||
|
while (prev->next_ != that)
|
||||||
|
prev = prev->next_;
|
||||||
|
|
||||||
|
prev->next_ = that->next_;
|
||||||
|
|
||||||
|
// If "that" was the last item in the list, then change the
|
||||||
|
// list_ pointer to point to the new end of the list.
|
||||||
|
if (list_ == that) {
|
||||||
|
assert(that->nexus_ == this);
|
||||||
|
list_ = prev;
|
||||||
|
list_->nexus_ = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
cur->next_ = that->next_;
|
|
||||||
that->nexus_ = 0;
|
that->nexus_ = 0;
|
||||||
that->next_ = 0;
|
that->next_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nexus::relink_(Link*that)
|
|
||||||
{
|
|
||||||
/* If the link I'm adding is a driver for this nexus, then
|
|
||||||
cancel my guess of the driven value. */
|
|
||||||
if (that->get_dir() != Link::INPUT)
|
|
||||||
driven_ = NO_GUESS;
|
|
||||||
|
|
||||||
assert(that->nexus_ == 0);
|
|
||||||
assert(that->next_ == 0);
|
|
||||||
that->next_ = list_;
|
|
||||||
that->nexus_ = this;
|
|
||||||
list_ = that;
|
|
||||||
}
|
|
||||||
|
|
||||||
Link* Nexus::first_nlink()
|
Link* Nexus::first_nlink()
|
||||||
{
|
{
|
||||||
return list_;
|
if (list_) return list_->next_;
|
||||||
|
else return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Link* Nexus::first_nlink() const
|
const Link* Nexus::first_nlink() const
|
||||||
{
|
{
|
||||||
return list_;
|
if (list_) return list_->next_;
|
||||||
|
else return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ivl_nexus_t Nexus::t_cookie() const
|
ivl_nexus_t Nexus::t_cookie() const
|
||||||
|
|
|
||||||
25
netlist.h
25
netlist.h
|
|
@ -168,9 +168,12 @@ class Link {
|
||||||
verinum::V init_ : 2;
|
verinum::V init_ : 2;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The Nexus uses these to maintain a single linked list of
|
Nexus* find_nexus_() const;
|
||||||
// Link objects. If this link is not connected to anything,
|
|
||||||
// then these pointers are nil.
|
private:
|
||||||
|
// The Nexus uses these to maintain its list of Link
|
||||||
|
// objects. If this link is not connected to anything,
|
||||||
|
// then these pointers are both nil.
|
||||||
Link *next_;
|
Link *next_;
|
||||||
Nexus*nexus_;
|
Nexus*nexus_;
|
||||||
|
|
||||||
|
|
@ -313,9 +316,11 @@ class NetBranch : public NetPins, public IslandBranch {
|
||||||
* together. Each link has its own properties, this class holds the
|
* together. Each link has its own properties, this class holds the
|
||||||
* properties of the group.
|
* properties of the group.
|
||||||
*
|
*
|
||||||
* The links in a nexus are grouped into a singly linked list, with
|
* The links in a nexus are grouped into a circularly linked list,
|
||||||
* the nexus pointing to the first Link. Each link in turn points to
|
* with the nexus pointing to the last Link. Each link in turn points
|
||||||
* the next link in the nexus, with the last link pointing to 0.
|
* to the next link in the nexus, with the last link pointing back to
|
||||||
|
* the first. The last link also has a non-nil nexus_ pointer back to
|
||||||
|
* this nexus.
|
||||||
*
|
*
|
||||||
* The t_cookie() is a void* that targets can use to store information
|
* The t_cookie() is a void* that targets can use to store information
|
||||||
* in a Nexus. ivl guarantees that the t_cookie will be 0 when the
|
* in a Nexus. ivl guarantees that the t_cookie will be 0 when the
|
||||||
|
|
@ -326,10 +331,13 @@ class Nexus {
|
||||||
friend void connect(Link&, Link&);
|
friend void connect(Link&, Link&);
|
||||||
friend class Link;
|
friend class Link;
|
||||||
|
|
||||||
public:
|
private:
|
||||||
explicit Nexus();
|
// Only Link objects can create (or delete) Nexus objects
|
||||||
|
explicit Nexus(Link&r);
|
||||||
~Nexus();
|
~Nexus();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
void connect(Link&r);
|
void connect(Link&r);
|
||||||
|
|
||||||
const char* name() const;
|
const char* name() const;
|
||||||
|
|
@ -371,7 +379,6 @@ class Nexus {
|
||||||
private:
|
private:
|
||||||
Link*list_;
|
Link*list_;
|
||||||
void unlink(Link*);
|
void unlink(Link*);
|
||||||
void relink_(Link*);
|
|
||||||
|
|
||||||
mutable char* name_; /* Cache the calculated name for the Nexus. */
|
mutable char* name_; /* Cache the calculated name for the Nexus. */
|
||||||
mutable ivl_nexus_t t_cookie_;
|
mutable ivl_nexus_t t_cookie_;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue