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
|
||||
* 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())
|
||||
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.
|
||||
verinum::V sel_val = sel_nex->driven_value();
|
||||
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
|
||||
* 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
|
||||
* the nexus has a known constant value. If there is a supply net,
|
||||
* then the nexus again has a known constant value.
|
||||
* the nexus has a known constant value.
|
||||
*/
|
||||
bool Nexus::drivers_constant() const
|
||||
{
|
||||
|
|
@ -35,15 +34,23 @@ bool Nexus::drivers_constant() const
|
|||
if (driven_ != NO_GUESS)
|
||||
return true;
|
||||
|
||||
unsigned constant_drivers = 0;
|
||||
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)
|
||||
continue;
|
||||
|
||||
/* 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
|
||||
can happen in cases like this:
|
||||
|
||||
|
|
@ -56,16 +63,9 @@ bool Nexus::drivers_constant() const
|
|||
outside world. */
|
||||
|
||||
if (cur_dir == Link::PASSIVE) {
|
||||
const NetNet*sig;
|
||||
|
||||
const NetPins*obj = cur->get_obj();
|
||||
const NetObj*as_obj = dynamic_cast<const NetObj*>(obj);
|
||||
if (as_obj == 0 || as_obj->scope()->parent() != 0)
|
||||
if (sig == 0 || sig->scope()->parent() != 0)
|
||||
continue;
|
||||
|
||||
sig = dynamic_cast<const NetNet*>(cur->get_obj());
|
||||
assert(sig);
|
||||
|
||||
if (sig->port_type() == NetNet::NOT_A_PORT)
|
||||
continue;
|
||||
|
||||
|
|
@ -74,19 +74,22 @@ bool Nexus::drivers_constant() const
|
|||
|
||||
driven_ = VAR;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/* If there is a supply net, then this nexus will have a
|
||||
constant value independent of any drivers. */
|
||||
if (const NetNet*s = dynamic_cast<const NetNet*>(cur->get_obj()))
|
||||
switch (s->type()) {
|
||||
/* If there is an implicit pullup/pulldown on a net,
|
||||
count it as a constant driver. */
|
||||
if (sig)
|
||||
switch (sig->type()) {
|
||||
case NetNet::SUPPLY0:
|
||||
case NetNet::TRI0:
|
||||
constant_drivers += 1;
|
||||
driven_ = V0;
|
||||
return true;
|
||||
continue;
|
||||
case NetNet::SUPPLY1:
|
||||
case NetNet::TRI1:
|
||||
constant_drivers += 1;
|
||||
driven_ = V1;
|
||||
return true;
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -95,6 +98,18 @@ bool Nexus::drivers_constant() const
|
|||
driven_ = VAR;
|
||||
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;
|
||||
|
|
@ -127,28 +142,25 @@ verinum::V Nexus::driven_value() const
|
|||
const NetConst*obj;
|
||||
const NetNet*sig;
|
||||
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());
|
||||
|
||||
} else if ((sig = dynamic_cast<const NetNet*>(cur->get_obj()))) {
|
||||
|
||||
// If we find an attached SUPPLY0/1, the we know
|
||||
// from that what the driven value is. Stop now.
|
||||
if (sig->type() == NetNet::SUPPLY0) {
|
||||
driven_ = V0;
|
||||
return verinum::V0;
|
||||
}
|
||||
if (sig->type() == NetNet::SUPPLY1) {
|
||||
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) {
|
||||
// If we find an implicit pullup or pulldown on a
|
||||
// net, this is a good guess for the driven value,
|
||||
// but keep looking for other drivers.
|
||||
if ((sig->type() == NetNet::SUPPLY0) ||
|
||||
(sig->type() == NetNet::TRI0)) {
|
||||
// Multiple drivers are not currently supported.
|
||||
ivl_assert(*obj, val == verinum::Vz);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -179,40 +191,44 @@ verinum Nexus::driven_vector() const
|
|||
const Link*cur = list_;
|
||||
|
||||
verinum val;
|
||||
unsigned width = 0;
|
||||
|
||||
for (cur = first_nlink() ; cur ; cur = cur->next_nlink()) {
|
||||
|
||||
const NetConst*obj;
|
||||
const NetNet*sig;
|
||||
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);
|
||||
val = obj->value();
|
||||
width = val.len();
|
||||
|
||||
} else if ((sig = dynamic_cast<const NetNet*>(cur->get_obj()))) {
|
||||
|
||||
// If we find an attached SUPPLY0/1, the we know
|
||||
// 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());
|
||||
}
|
||||
width = sig->vector_width();
|
||||
|
||||
// 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(verinum::V0, sig->vector_width());
|
||||
// If we find an implicit pullup or pulldown on a
|
||||
// net, this is a good guess for the driven value,
|
||||
// but keep looking for other drivers.
|
||||
if ((sig->type() == NetNet::SUPPLY0) ||
|
||||
(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) {
|
||||
val = verinum(verinum::V1, sig->vector_width());
|
||||
if ((sig->type() == NetNet::SUPPLY1) ||
|
||||
(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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
|
|
@ -311,16 +311,13 @@ bool Nexus::drivers_present() const
|
|||
continue;
|
||||
|
||||
// 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;
|
||||
unsigned pin;
|
||||
cur->cur_link(obj, pin);
|
||||
if (const NetNet*net = dynamic_cast<const NetNet*>(obj))
|
||||
switch (net->type()) {
|
||||
case NetNet::SUPPLY0:
|
||||
case NetNet::SUPPLY1:
|
||||
case NetNet::TRI0:
|
||||
case NetNet::TRI1:
|
||||
case NetNet::WAND:
|
||||
case NetNet::WOR:
|
||||
case NetNet::TRIAND:
|
||||
|
|
|
|||
111
netlist.cc
111
netlist.cc
|
|
@ -479,8 +479,23 @@ PortType::Enum PortType::merged( Enum lhs, Enum rhs )
|
|||
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 (0) cerr << "NetNet setting Link default dir" << endl;
|
||||
set_default_dir(dir);
|
||||
|
|
@ -564,24 +579,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t,
|
|||
ivl_assert(*this, 0);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
initialize_dir_(dir);
|
||||
initialize_dir_();
|
||||
|
||||
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));
|
||||
calculate_slice_widths_from_packed_dims_();
|
||||
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;
|
||||
}
|
||||
|
||||
initialize_dir_(dir);
|
||||
initialize_dir_();
|
||||
|
||||
s->add_signal(this);
|
||||
}
|
||||
|
|
@ -629,24 +611,7 @@ NetNet::NetNet(NetScope*s, perm_string n, Type t, netdarray_t*ty)
|
|||
discipline_(0),
|
||||
eref_count_(0), lref_count_(0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
initialize_dir_(dir);
|
||||
initialize_dir_();
|
||||
|
||||
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)
|
||||
{
|
||||
calculate_slice_widths_from_packed_dims_();
|
||||
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;
|
||||
}
|
||||
|
||||
initialize_dir_(dir);
|
||||
initialize_dir_();
|
||||
|
||||
s->add_signal(this);
|
||||
}
|
||||
|
|
@ -712,27 +661,9 @@ void NetNet::type(NetNet::Type t)
|
|||
if (type_ == t)
|
||||
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;
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
|
|
@ -746,7 +747,7 @@ class NetNet : public NetObj, public PortType {
|
|||
virtual void dump_net(ostream&, unsigned) const;
|
||||
|
||||
private:
|
||||
void initialize_dir_(Link::DIR dir);
|
||||
void initialize_dir_();
|
||||
|
||||
private:
|
||||
Type type_ : 5;
|
||||
|
|
|
|||
Loading…
Reference in New Issue