NetCondit synthesis accounts for smaller l-values

If both conditions of a NetCondit device assign to the same subset
of l-value bits, then generate a smaller NetMux device that only
switches the affected bits.
This commit is contained in:
Stephen Williams 2014-05-24 20:08:48 -07:00
parent f16c67d45a
commit 76883fa18c
3 changed files with 129 additions and 4 deletions

View File

@ -1354,3 +1354,68 @@ void assign_unpacked_with_bufz(Design*des, NetScope*scope,
}
}
/*
* synthesis sometimes needs to unpack assignment to a part
* select. That looks like this:
*
* foo[N] <= <expr> ;
*
* The NetAssignBase::synth_async() method will turn that into a
* netlist like this:
*
* NetAssignBase(PV) --> base()==<N>
* (0) (1)
* | |
* v v
* <expr> foo
*
* This search will return a pointer to the NetAssignBase(PV) object,
* but only if it matches this pattern.
*/
NetPartSelect* detect_partselect_lval(Link&pin)
{
NetPartSelect*found_ps = 0;
Nexus*nex = pin.nexus();
for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) {
NetPins*obj;
unsigned obj_pin;
cur->cur_link(obj, obj_pin);
// NetNet pins have no effect on this search.
if (dynamic_cast<NetNet*> (obj))
continue;
if (NetPartSelect*ps = dynamic_cast<NetPartSelect*> (obj)) {
// If this is the input side of a NetPartSelect, skip.
if (ps->pin(obj_pin).get_dir()==Link::INPUT)
continue;
// Oops, driven by the wrong size of a
// NetPartSelect, so this is not going to work out.
if (ps->dir()==NetPartSelect::VP)
return 0;
// So now we know this is a NetPartSelect::PV. It
// is a candidate for our part-select assign. If
// we already have a candidate, then give up.
if (found_ps)
return 0;
// This is our candidate. Carry on.
found_ps = ps;
continue;
}
// If this is a driver to the Nexus that is not a
// NetPartSelect device. This cannot happen to
// part selected lval nets, so quit now.
if (obj->pin(obj_pin).get_dir() == Link::OUTPUT)
return 0;
}
return found_ps;
}

View File

@ -353,4 +353,6 @@ extern void assign_unpacked_with_bufz(Design*des, NetScope*scope,
const LineInfo*loc,
NetNet*lval, NetNet*rval);
extern NetPartSelect* detect_partselect_lval(Link&pin);
#endif

View File

@ -105,7 +105,7 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope,
if (lval_->lwidth() != lsig->vector_width()) {
ivl_assert(*this, lval_->lwidth() < lsig->vector_width());
// XXXX If we ar within a NetForLoop or similar
// XXXX If we are within a NetForLoop or similar
// processing, then there may be an index value. I
// currently do not know how to handle that, but
// probably I'm going to need the index_args passed in.
@ -481,6 +481,12 @@ bool NetCondit::synth_async(Design*des, NetScope*scope,
}
}
/* The nex_out output, asig input, and bsig input all have the
same pin count (usually, but not always 1) because they are
net arrays of the same dimension. The for loop below creates
a NetMux for each pin of the output. (Note that pins may
be, in fact usually are, vectors.) */
ivl_assert(*this, nex_out.pin_count()==asig.pin_count());
ivl_assert(*this, nex_out.pin_count()==bsig.pin_count());
@ -493,13 +499,51 @@ bool NetCondit::synth_async(Design*des, NetScope*scope,
if (NetNet*tmp = nex_out.pin(idx).nexus()->pick_any_net()) {
mux_data_type = tmp->data_type();
}
unsigned mux_off = 0;
unsigned mux_width = asig.pin(idx).nexus()->vector_width();
const unsigned mux_lwidth = mux_width;
ivl_assert(*this, mux_width != 0);
NetPartSelect*apv = detect_partselect_lval(asig.pin(idx));
if (debug_synth2 && apv) {
cerr << get_fileline() << ": NetCondit::synth_async: "
<< "Assign-to-part apv base=" << apv->base()
<< ", width=" << apv->width() << endl;
}
NetPartSelect*bpv = detect_partselect_lval(bsig.pin(idx));
if (debug_synth2 && bpv) {
cerr << get_fileline() << ": NetCondit::synth_async: "
<< "Assign-to-part bpv base=" << bpv->base()
<< ", width=" << bpv->width() << endl;
}
if (apv && bpv && apv->width()==bpv->width() && apv->base()==bpv->base()) {
// The a and b sides are both assigning to the
// same bits of the output, so we can use that to
// create a much narrower mux that only
// manipulates the width of the part.
mux_width = apv->width();
mux_off = apv->base();
asig.pin(idx).unlink();
bsig.pin(idx).unlink();
connect(asig.pin(idx), apv->pin(0));
connect(bsig.pin(idx), bpv->pin(0));
delete apv;
delete bpv;
} else {
// The part selects are of no use. Forget them.
apv = 0;
bpv = 0;
}
if (mux_width != bsig.pin(idx).nexus()->vector_width()) {
cerr << get_fileline() << ": internal error: "
<< "NetCondit::synth_async: "
<< "Mux input sizes do not match."
<< " A size=" << mux_width
<< " A size=" << mux_lwidth
<< ", B size=" << bsig.pin(idx).nexus()->vector_width()
<< endl;
cerr << get_fileline() << ": : "
@ -533,12 +577,26 @@ bool NetCondit::synth_async(Design*des, NetScope*scope,
NetNet*otmp = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, not_an_array, tmp_type);
otmp->local_flag(true);
connect(nex_out.pin(idx),otmp->pin(0));
connect(mux->pin_Result(),otmp->pin(0));
connect(mux->pin_Sel(), ssig->pin(0));
connect(mux->pin_Data(1), asig.pin(idx));
connect(mux->pin_Data(0), bsig.pin(idx));
connect(nex_out.pin(idx), mux->pin_Result());
// We are only muxing a part of the output vector, so
// make a NetPartSelect::PV to widen the vector to the
// output at hand.
if (mux_width < mux_lwidth) {
tmp_type = new netvector_t(mux_data_type, mux_lwidth-1,0);
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, not_an_array, tmp_type);
NetPartSelect*ps = new NetPartSelect(tmp, mux_off, mux_width, NetPartSelect::PV);
des->add_node(ps);
connect(ps->pin(0), otmp->pin(0));
otmp = tmp;
}
connect(nex_out.pin(idx), otmp->pin(0));
des->add_node(mux);
}