Fix errors in constant propagation.
Ensure undriven, multiply-driven, and forced nets are correctly handled by the constant propagation optimisation pass.
This commit is contained in:
parent
1814c24a90
commit
d291e2b23d
7
cprop.cc
7
cprop.cc
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1998-2010,2012 Stephen Williams (steve@icarus.com)
|
* Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com)
|
||||||
*
|
*
|
||||||
* This source code is free software; you can redistribute it
|
* This source code is free software; you can redistribute it
|
||||||
* and/or modify it in source code form under the terms of the GNU
|
* and/or modify it in source code form under the terms of the GNU
|
||||||
|
|
@ -163,11 +163,6 @@ void cprop_functor::lpm_mux(Design*des, NetMux*obj)
|
||||||
if (! sel_nex->drivers_constant())
|
if (! sel_nex->drivers_constant())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If the select input is assigned or forced, then again there
|
|
||||||
// is nothing we can do here.
|
|
||||||
if (sel_nex->assign_lval())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If the constant select is 'bz or 'bx, then give up.
|
// If the constant select is 'bz or 'bx, then give up.
|
||||||
verinum::V sel_val = sel_nex->driven_value();
|
verinum::V sel_val = sel_nex->driven_value();
|
||||||
if (sel_val == verinum::Vz || sel_val == verinum::Vx)
|
if (sel_val == verinum::Vz || sel_val == verinum::Vx)
|
||||||
|
|
|
||||||
126
link_const.cc
126
link_const.cc
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2000-2012 Stephen Williams (steve@icarus.com)
|
* Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com)
|
||||||
*
|
*
|
||||||
* This source code is free software; you can redistribute it
|
* This source code is free software; you can redistribute it
|
||||||
* and/or modify it in source code form under the terms of the GNU
|
* and/or modify it in source code form under the terms of the GNU
|
||||||
|
|
@ -25,8 +25,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan the link for drivers. If there are only constant drivers, then
|
* Scan the link for drivers. If there are only constant drivers, then
|
||||||
* the nexus has a known constant value. If there is a supply net,
|
* the nexus has a known constant value.
|
||||||
* then the nexus again has a known constant value.
|
|
||||||
*/
|
*/
|
||||||
bool Nexus::drivers_constant() const
|
bool Nexus::drivers_constant() const
|
||||||
{
|
{
|
||||||
|
|
@ -35,15 +34,23 @@ bool Nexus::drivers_constant() const
|
||||||
if (driven_ != NO_GUESS)
|
if (driven_ != NO_GUESS)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
unsigned constant_drivers = 0;
|
||||||
for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
for (const Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||||
Link::DIR cur_dir;
|
|
||||||
|
|
||||||
cur_dir = cur->get_dir();
|
/* A target of a procedural assign or force statement
|
||||||
|
can't be treated as constant. */
|
||||||
|
const NetNet*sig = dynamic_cast<const NetNet*>(cur->get_obj());
|
||||||
|
if (sig && (sig->peek_lref() > 0)) {
|
||||||
|
driven_ = VAR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Link::DIR cur_dir = cur->get_dir();
|
||||||
if (cur_dir == Link::INPUT)
|
if (cur_dir == Link::INPUT)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* If this is an input or inout port of a root module,
|
/* If this is an input or inout port of a root module,
|
||||||
then the is probably not a constant value. I
|
then this is probably not a constant value. I
|
||||||
certainly don't know what the value is, anyhow. This
|
certainly don't know what the value is, anyhow. This
|
||||||
can happen in cases like this:
|
can happen in cases like this:
|
||||||
|
|
||||||
|
|
@ -56,16 +63,9 @@ bool Nexus::drivers_constant() const
|
||||||
outside world. */
|
outside world. */
|
||||||
|
|
||||||
if (cur_dir == Link::PASSIVE) {
|
if (cur_dir == Link::PASSIVE) {
|
||||||
const NetNet*sig;
|
if (sig == 0 || sig->scope()->parent() != 0)
|
||||||
|
|
||||||
const NetPins*obj = cur->get_obj();
|
|
||||||
const NetObj*as_obj = dynamic_cast<const NetObj*>(obj);
|
|
||||||
if (as_obj == 0 || as_obj->scope()->parent() != 0)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
sig = dynamic_cast<const NetNet*>(cur->get_obj());
|
|
||||||
assert(sig);
|
|
||||||
|
|
||||||
if (sig->port_type() == NetNet::NOT_A_PORT)
|
if (sig->port_type() == NetNet::NOT_A_PORT)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -74,19 +74,22 @@ bool Nexus::drivers_constant() const
|
||||||
|
|
||||||
driven_ = VAR;
|
driven_ = VAR;
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If there is a supply net, then this nexus will have a
|
/* If there is an implicit pullup/pulldown on a net,
|
||||||
constant value independent of any drivers. */
|
count it as a constant driver. */
|
||||||
if (const NetNet*s = dynamic_cast<const NetNet*>(cur->get_obj()))
|
if (sig)
|
||||||
switch (s->type()) {
|
switch (sig->type()) {
|
||||||
case NetNet::SUPPLY0:
|
case NetNet::SUPPLY0:
|
||||||
|
case NetNet::TRI0:
|
||||||
|
constant_drivers += 1;
|
||||||
driven_ = V0;
|
driven_ = V0;
|
||||||
return true;
|
continue;
|
||||||
case NetNet::SUPPLY1:
|
case NetNet::SUPPLY1:
|
||||||
|
case NetNet::TRI1:
|
||||||
|
constant_drivers += 1;
|
||||||
driven_ = V1;
|
driven_ = V1;
|
||||||
return true;
|
continue;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -95,6 +98,18 @@ bool Nexus::drivers_constant() const
|
||||||
driven_ = VAR;
|
driven_ = VAR;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constant_drivers += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there is more than one constant driver for this nexus, we
|
||||||
|
would need to resolve the constant value, taking into account
|
||||||
|
the drive strengths. This is a lot of work for something that
|
||||||
|
will rarely occur, so for now leave the resolution to be done
|
||||||
|
at run time. */
|
||||||
|
if (constant_drivers > 1) {
|
||||||
|
driven_ = VAR;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -127,28 +142,25 @@ verinum::V Nexus::driven_value() const
|
||||||
const NetConst*obj;
|
const NetConst*obj;
|
||||||
const NetNet*sig;
|
const NetNet*sig;
|
||||||
if ((obj = dynamic_cast<const NetConst*>(cur->get_obj()))) {
|
if ((obj = dynamic_cast<const NetConst*>(cur->get_obj()))) {
|
||||||
|
// Multiple drivers are not currently supported.
|
||||||
|
ivl_assert(*obj, val == verinum::Vz);
|
||||||
val = obj->value(cur->get_pin());
|
val = obj->value(cur->get_pin());
|
||||||
|
|
||||||
} else if ((sig = dynamic_cast<const NetNet*>(cur->get_obj()))) {
|
} else if ((sig = dynamic_cast<const NetNet*>(cur->get_obj()))) {
|
||||||
|
|
||||||
// If we find an attached SUPPLY0/1, the we know
|
// If we find an implicit pullup or pulldown on a
|
||||||
// from that what the driven value is. Stop now.
|
// net, this is a good guess for the driven value,
|
||||||
if (sig->type() == NetNet::SUPPLY0) {
|
// but keep looking for other drivers.
|
||||||
driven_ = V0;
|
if ((sig->type() == NetNet::SUPPLY0) ||
|
||||||
return verinum::V0;
|
(sig->type() == NetNet::TRI0)) {
|
||||||
}
|
// Multiple drivers are not currently supported.
|
||||||
if (sig->type() == NetNet::SUPPLY1) {
|
ivl_assert(*obj, val == verinum::Vz);
|
||||||
driven_ = V1;
|
|
||||||
return verinum::V1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we find an attached TRI0/1, then this is a
|
|
||||||
// good guess for the driven value, but keep
|
|
||||||
// looking for something better.
|
|
||||||
if (sig->type() == NetNet::TRI0) {
|
|
||||||
val = verinum::V0;
|
val = verinum::V0;
|
||||||
}
|
}
|
||||||
if (sig->type() == NetNet::TRI1) {
|
if ((sig->type() == NetNet::SUPPLY1) ||
|
||||||
|
(sig->type() == NetNet::TRI1)) {
|
||||||
|
// Multiple drivers are not currently supported.
|
||||||
|
ivl_assert(*obj, val == verinum::Vz);
|
||||||
val = verinum::V1;
|
val = verinum::V1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -179,40 +191,44 @@ verinum Nexus::driven_vector() const
|
||||||
const Link*cur = list_;
|
const Link*cur = list_;
|
||||||
|
|
||||||
verinum val;
|
verinum val;
|
||||||
|
unsigned width = 0;
|
||||||
|
|
||||||
for (cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
for (cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||||
|
|
||||||
const NetConst*obj;
|
const NetConst*obj;
|
||||||
const NetNet*sig;
|
const NetNet*sig;
|
||||||
if ((obj = dynamic_cast<const NetConst*>(cur->get_obj()))) {
|
if ((obj = dynamic_cast<const NetConst*>(cur->get_obj()))) {
|
||||||
|
// Multiple drivers are not currently supported.
|
||||||
|
ivl_assert(*obj, val.len() == 0);
|
||||||
ivl_assert(*obj, cur->get_pin() == 0);
|
ivl_assert(*obj, cur->get_pin() == 0);
|
||||||
val = obj->value();
|
val = obj->value();
|
||||||
|
width = val.len();
|
||||||
|
|
||||||
} else if ((sig = dynamic_cast<const NetNet*>(cur->get_obj()))) {
|
} else if ((sig = dynamic_cast<const NetNet*>(cur->get_obj()))) {
|
||||||
|
|
||||||
// If we find an attached SUPPLY0/1, the we know
|
width = sig->vector_width();
|
||||||
// from that what the driven value is. Stop now.
|
|
||||||
if (sig->type() == NetNet::SUPPLY0) {
|
|
||||||
driven_ = V0;
|
|
||||||
return verinum(verinum::V0, sig->vector_width());
|
|
||||||
}
|
|
||||||
if (sig->type() == NetNet::SUPPLY1) {
|
|
||||||
driven_ = V1;
|
|
||||||
return verinum(verinum::V1, sig->vector_width());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we find an attached TRI0/1, then this is a
|
// If we find an implicit pullup or pulldown on a
|
||||||
// good guess for the driven value, but keep
|
// net, this is a good guess for the driven value,
|
||||||
// looking for something better.
|
// but keep looking for other drivers.
|
||||||
if (sig->type() == NetNet::TRI0) {
|
if ((sig->type() == NetNet::SUPPLY0) ||
|
||||||
val = verinum(verinum::V0, sig->vector_width());
|
(sig->type() == NetNet::TRI0)) {
|
||||||
|
// Multiple drivers are not currently supported.
|
||||||
|
ivl_assert(*obj, val.len() == 0);
|
||||||
|
val = verinum(verinum::V0, width);
|
||||||
}
|
}
|
||||||
if (sig->type() == NetNet::TRI1) {
|
if ((sig->type() == NetNet::SUPPLY1) ||
|
||||||
val = verinum(verinum::V1, sig->vector_width());
|
(sig->type() == NetNet::TRI1)) {
|
||||||
|
// Multiple drivers are not currently supported.
|
||||||
|
ivl_assert(*obj, val.len() == 0);
|
||||||
|
val = verinum(verinum::V1, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have a width but not a value, this must be an undriven net.
|
||||||
|
if (val.len() != width)
|
||||||
|
val = verinum(verinum::Vz, width);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com)
|
* Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com)
|
||||||
*
|
*
|
||||||
* This source code is free software; you can redistribute it
|
* This source code is free software; you can redistribute it
|
||||||
* and/or modify it in source code form under the terms of the GNU
|
* and/or modify it in source code form under the terms of the GNU
|
||||||
|
|
@ -311,16 +311,13 @@ bool Nexus::drivers_present() const
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Must be PASSIVE, so if it is some kind of net, see if
|
// Must be PASSIVE, so if it is some kind of net, see if
|
||||||
// it is the sort that might drive the nexus.
|
// it is the sort that might drive the nexus. Note that
|
||||||
|
// supply0/1 and tri0/1 nets are classified as OUTPUT.
|
||||||
const NetPins*obj;
|
const NetPins*obj;
|
||||||
unsigned pin;
|
unsigned pin;
|
||||||
cur->cur_link(obj, pin);
|
cur->cur_link(obj, pin);
|
||||||
if (const NetNet*net = dynamic_cast<const NetNet*>(obj))
|
if (const NetNet*net = dynamic_cast<const NetNet*>(obj))
|
||||||
switch (net->type()) {
|
switch (net->type()) {
|
||||||
case NetNet::SUPPLY0:
|
|
||||||
case NetNet::SUPPLY1:
|
|
||||||
case NetNet::TRI0:
|
|
||||||
case NetNet::TRI1:
|
|
||||||
case NetNet::WAND:
|
case NetNet::WAND:
|
||||||
case NetNet::WOR:
|
case NetNet::WOR:
|
||||||
case NetNet::TRIAND:
|
case NetNet::TRIAND:
|
||||||
|
|
|
||||||
111
netlist.cc
111
netlist.cc
|
|
@ -479,8 +479,23 @@ PortType::Enum PortType::merged( Enum lhs, Enum rhs )
|
||||||
return PINOUT;
|
return PINOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetNet::initialize_dir_(Link::DIR dir)
|
void NetNet::initialize_dir_()
|
||||||
{
|
{
|
||||||
|
Link::DIR dir = Link::PASSIVE;
|
||||||
|
|
||||||
|
switch (type_) {
|
||||||
|
case REG:
|
||||||
|
case IMPLICIT_REG:
|
||||||
|
case SUPPLY0:
|
||||||
|
case SUPPLY1:
|
||||||
|
case TRI0:
|
||||||
|
case TRI1:
|
||||||
|
dir = Link::OUTPUT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (pins_are_virtual()) {
|
if (pins_are_virtual()) {
|
||||||
if (0) cerr << "NetNet setting Link default dir" << endl;
|
if (0) cerr << "NetNet setting Link default dir" << endl;
|
||||||
set_default_dir(dir);
|
set_default_dir(dir);
|
||||||
|
|
@ -564,24 +579,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t,
|
||||||
ivl_assert(*this, 0);
|
ivl_assert(*this, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Link::DIR dir = Link::PASSIVE;
|
initialize_dir_();
|
||||||
|
|
||||||
switch (t) {
|
|
||||||
case REG:
|
|
||||||
case IMPLICIT_REG:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
case SUPPLY0:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
case SUPPLY1:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize_dir_(dir);
|
|
||||||
|
|
||||||
s->add_signal(this);
|
s->add_signal(this);
|
||||||
}
|
}
|
||||||
|
|
@ -600,24 +598,8 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, netstruct_t*ty)
|
||||||
{
|
{
|
||||||
//XXXX packed_dims_.push_back(netrange_t(calculate_count(ty)-1, 0));
|
//XXXX packed_dims_.push_back(netrange_t(calculate_count(ty)-1, 0));
|
||||||
calculate_slice_widths_from_packed_dims_();
|
calculate_slice_widths_from_packed_dims_();
|
||||||
Link::DIR dir = Link::PASSIVE;
|
|
||||||
|
|
||||||
switch (t) {
|
initialize_dir_();
|
||||||
case REG:
|
|
||||||
case IMPLICIT_REG:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
case SUPPLY0:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
case SUPPLY1:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize_dir_(dir);
|
|
||||||
|
|
||||||
s->add_signal(this);
|
s->add_signal(this);
|
||||||
}
|
}
|
||||||
|
|
@ -629,24 +611,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, netdarray_t*ty)
|
||||||
discipline_(0),
|
discipline_(0),
|
||||||
eref_count_(0), lref_count_(0)
|
eref_count_(0), lref_count_(0)
|
||||||
{
|
{
|
||||||
Link::DIR dir = Link::PASSIVE;
|
initialize_dir_();
|
||||||
|
|
||||||
switch (t) {
|
|
||||||
case REG:
|
|
||||||
case IMPLICIT_REG:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
case SUPPLY0:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
case SUPPLY1:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize_dir_(dir);
|
|
||||||
|
|
||||||
s->add_signal(this);
|
s->add_signal(this);
|
||||||
}
|
}
|
||||||
|
|
@ -659,24 +624,8 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, netvector_t*ty)
|
||||||
eref_count_(0), lref_count_(0)
|
eref_count_(0), lref_count_(0)
|
||||||
{
|
{
|
||||||
calculate_slice_widths_from_packed_dims_();
|
calculate_slice_widths_from_packed_dims_();
|
||||||
Link::DIR dir = Link::PASSIVE;
|
|
||||||
|
|
||||||
switch (t) {
|
initialize_dir_();
|
||||||
case REG:
|
|
||||||
case IMPLICIT_REG:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
case SUPPLY0:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
case SUPPLY1:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize_dir_(dir);
|
|
||||||
|
|
||||||
s->add_signal(this);
|
s->add_signal(this);
|
||||||
}
|
}
|
||||||
|
|
@ -712,27 +661,9 @@ void NetNet::type(NetNet::Type t)
|
||||||
if (type_ == t)
|
if (type_ == t)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Link::DIR dir = Link::PASSIVE;
|
|
||||||
switch (t) {
|
|
||||||
case REG:
|
|
||||||
case IMPLICIT_REG:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
case SUPPLY0:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
case SUPPLY1:
|
|
||||||
dir = Link::OUTPUT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
type_ = t;
|
type_ = t;
|
||||||
for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) {
|
|
||||||
pin(idx).set_dir(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
initialize_dir_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -581,9 +581,10 @@ class NetDelaySrc : public NetObj {
|
||||||
*
|
*
|
||||||
* NetNet objects are located by searching NetScope objects.
|
* NetNet objects are located by searching NetScope objects.
|
||||||
*
|
*
|
||||||
* The pins of a NetNet object are PASSIVE: they do not drive
|
* The pins of a NetNet object are usually PASSIVE: they do not drive
|
||||||
* anything and they are not a data sink, per se. The pins follow the
|
* anything and they are not a data sink, per se. The pins follow the
|
||||||
* values on the nexus.
|
* values on the nexus. The exceptions are reg, trireg, tri0, tri1,
|
||||||
|
* supply0, and supply1 objects, whose pins are classed as OUTPUT.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class PortType
|
class PortType
|
||||||
|
|
@ -746,7 +747,7 @@ class NetNet : public NetObj, public PortType {
|
||||||
virtual void dump_net(ostream&, unsigned) const;
|
virtual void dump_net(ostream&, unsigned) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initialize_dir_(Link::DIR dir);
|
void initialize_dir_();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type type_ : 5;
|
Type type_ : 5;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue