Handle synthesis of fully packed case statements.
This commit is contained in:
parent
5cf2ce6499
commit
dac11c2468
|
|
@ -17,7 +17,7 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: design_dump.cc,v 1.162 2005/07/11 16:56:50 steve Exp $"
|
||||
#ident "$Id: design_dump.cc,v 1.163 2005/08/27 04:32:08 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "config.h"
|
||||
|
|
@ -256,6 +256,7 @@ void NetMult::dump_node(ostream&o, unsigned ind) const
|
|||
void NetMux::dump_node(ostream&o, unsigned ind) const
|
||||
{
|
||||
o << setw(ind) << "" << "Multiplexer (NetMux): " << name()
|
||||
<< " width=" << width_ << " swidth=" << swidth_ << " size=" << size_
|
||||
<< " scope=" << scope()->name() << endl;
|
||||
dump_node_pins(o, ind+4);
|
||||
dump_obj_attr(o, ind+4);
|
||||
|
|
@ -1165,6 +1166,9 @@ void Design::dump(ostream&o) const
|
|||
|
||||
/*
|
||||
* $Log: design_dump.cc,v $
|
||||
* Revision 1.163 2005/08/27 04:32:08 steve
|
||||
* Handle synthesis of fully packed case statements.
|
||||
*
|
||||
* Revision 1.162 2005/07/11 16:56:50 steve
|
||||
* Remove NetVariable and ivl_variable_t structures.
|
||||
*
|
||||
|
|
|
|||
169
synth2.cc
169
synth2.cc
|
|
@ -17,7 +17,7 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: synth2.cc,v 1.44 2005/05/15 04:45:50 steve Exp $"
|
||||
#ident "$Id: synth2.cc,v 1.45 2005/08/27 04:32:08 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "config.h"
|
||||
|
|
@ -178,136 +178,86 @@ bool NetBlock::synth_async(Design*des, NetScope*scope,
|
|||
bool NetCase::synth_async(Design*des, NetScope*scope,
|
||||
const NetBus&nex_map, NetBus&nex_out)
|
||||
{
|
||||
#if 0
|
||||
unsigned cur;
|
||||
|
||||
/* Synthesize the select expression. */
|
||||
NetNet*esig = expr_->synthesize(des);
|
||||
|
||||
/* Scan the select vector looking for constant bits. The
|
||||
constant bits will be elided from the select input connect,
|
||||
but we still need to keep track of them. */
|
||||
unsigned sel_pins = 0;
|
||||
unsigned long sel_mask = 0;
|
||||
unsigned long sel_ref = 0;
|
||||
for (unsigned idx = 0 ; idx < esig->pin_count() ; idx += 1) {
|
||||
unsigned sel_width = esig->vector_width();
|
||||
assert(sel_width > 0);
|
||||
|
||||
if (esig->pin(idx).nexus()->drivers_constant()) {
|
||||
verinum::V bit = esig->pin(idx).nexus()->driven_value();
|
||||
if (bit == verinum::V1)
|
||||
sel_ref |= 1 << idx;
|
||||
unsigned mux_width = 0;
|
||||
for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1)
|
||||
mux_width += nex_out.pin(idx).nexus()->vector_width();
|
||||
|
||||
} else {
|
||||
sel_pins += 1;
|
||||
sel_mask |= 1 << idx;
|
||||
}
|
||||
}
|
||||
/* Collect all the statements into a map of index to
|
||||
statement. The guard expression it evaluated to be the
|
||||
index of the mux value, and the statement is bound to that
|
||||
index. */
|
||||
|
||||
/* Build a map of guard values to mux select values. This
|
||||
helps account for constant select bits that are being
|
||||
elided. */
|
||||
map<unsigned long,unsigned long>guard2sel;
|
||||
cur = 0;
|
||||
for (unsigned idx = 0 ; idx < (1U<<esig->vector_width()) ; idx += 1) {
|
||||
if ((idx & ~sel_mask) == sel_ref) {
|
||||
guard2sel[idx] = cur;
|
||||
cur += 1;
|
||||
}
|
||||
}
|
||||
assert(cur == (1U << sel_pins));
|
||||
unsigned long max_guard_value = 0;
|
||||
map<unsigned long,NetProc*>statement_map;
|
||||
NetProc*statement_default = 0;
|
||||
|
||||
NetMux*mux = new NetMux(scope, scope->local_symbol(),
|
||||
nex_out->pin_count(),
|
||||
1U << sel_pins, sel_pins);
|
||||
|
||||
/* Connect the non-constant select bits to the select input of
|
||||
the mux device. */
|
||||
cur = 0;
|
||||
for (unsigned idx = 0 ; idx < esig->pin_count() ; idx += 1) {
|
||||
/* skip bits that are known to be constant. */
|
||||
if ((sel_mask & (1U << idx)) == 0)
|
||||
continue;
|
||||
|
||||
connect(mux->pin_Sel(cur), esig->pin(idx));
|
||||
cur += 1;
|
||||
}
|
||||
assert(cur == sel_pins);
|
||||
|
||||
/* Hook up the output of the mux to the mapped output pins. */
|
||||
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1)
|
||||
connect(nex_out->pin(idx), mux->pin_Result(idx));
|
||||
|
||||
NetProc**statement_map = new NetProc*[1 << sel_pins];
|
||||
for (unsigned item = 0 ; item < (1U<<sel_pins) ; item += 1)
|
||||
statement_map[item] = 0;
|
||||
|
||||
/* Assign the input statements to MUX inputs. This involves
|
||||
calculating the guard value, passing that through the
|
||||
guard2sel map, then saving the statement in the
|
||||
statement_map. If I find a default case, then save that for
|
||||
use later. */
|
||||
NetProc*default_statement = 0;
|
||||
for (unsigned item = 0 ; item < nitems_ ; item += 1) {
|
||||
/* Skip the default case this pass. */
|
||||
if (items_[item].guard == 0) {
|
||||
default_statement = items_[item].statement;
|
||||
statement_default = items_[item].statement;
|
||||
continue;
|
||||
}
|
||||
|
||||
NetEConst*ge = dynamic_cast<NetEConst*>(items_[item].guard);
|
||||
assert(ge);
|
||||
verinum gval = ge->value();
|
||||
unsigned sel_idx = guard2sel[gval.as_ulong()];
|
||||
|
||||
unsigned sel_idx = gval.as_ulong();
|
||||
|
||||
assert(items_[item].statement);
|
||||
statement_map[sel_idx] = items_[item].statement;
|
||||
|
||||
if (sel_idx > max_guard_value)
|
||||
max_guard_value = sel_idx;
|
||||
}
|
||||
|
||||
/* Now that statements match with mux inputs, synthesize the
|
||||
sub-statements. If I get to an input that has no statement,
|
||||
then use the default statement there. */
|
||||
NetNet*default_sig = 0;
|
||||
for (unsigned item = 0 ; item < (1U<<sel_pins) ; item += 1) {
|
||||
unsigned mux_size = max_guard_value + 1;
|
||||
|
||||
/* Detect the case that this is a default input, and I
|
||||
have a precalculated default_sig. */
|
||||
if ((statement_map[item] == 0) && (default_sig != 0)) {
|
||||
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1)
|
||||
connect(mux->pin_Data(idx, item), default_sig->pin(idx));
|
||||
continue;
|
||||
}
|
||||
|
||||
NetNet*sig = new NetNet(scope, scope->local_symbol(),
|
||||
NetNet::WIRE, nex_map->pin_count());
|
||||
sig->local_flag(true);
|
||||
|
||||
if (statement_map[item] == 0) {
|
||||
statement_map[item] = default_statement;
|
||||
default_statement = 0;
|
||||
default_sig = sig;
|
||||
}
|
||||
|
||||
if (statement_map[item] == 0) {
|
||||
/* Missing case and no default; this could still be
|
||||
* synthesizable with synchronous logic, but not here. */
|
||||
DEBUG_SYNTH2_EXIT("NetCase", false)
|
||||
return false;
|
||||
}
|
||||
statement_map[item]->synth_async(des, scope, nex_map, sig);
|
||||
|
||||
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1)
|
||||
connect(mux->pin_Data(idx, item), sig->pin(idx));
|
||||
}
|
||||
|
||||
delete[]statement_map;
|
||||
NetMux*mux = new NetMux(scope, scope->local_symbol(),
|
||||
mux_width, mux_size, sel_width);
|
||||
des->add_node(mux);
|
||||
|
||||
DEBUG_SYNTH2_EXIT("NetCase", true)
|
||||
/* The select signal is already synthesized. Simply hook it up. */
|
||||
connect(mux->pin_Sel(), esig->pin(0));
|
||||
|
||||
/* For now, assume that the output is only 1 signal. */
|
||||
assert(nex_out.pin_count() == 1);
|
||||
connect(mux->pin_Result(), nex_out.pin(0));
|
||||
|
||||
/* For now, only support logic types. */
|
||||
ivl_variable_type_t mux_data_type = IVL_VT_LOGIC;
|
||||
|
||||
/* Forgot to support default statements? */
|
||||
assert(statement_default == 0);
|
||||
|
||||
NetNet*isig;
|
||||
for (unsigned idx = 0 ; idx < mux_size ; idx += 1) {
|
||||
|
||||
NetProc*stmt = statement_map[idx];
|
||||
if (stmt == 0) {
|
||||
cerr << get_line() << ": error: case " << idx
|
||||
<< " is not accounted for in asynchronous mux." << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
isig = new NetNet(scope, scope->local_symbol(),
|
||||
NetNet::TRI, mux_width);
|
||||
isig->local_flag(true);
|
||||
isig->data_type(mux_data_type);
|
||||
|
||||
connect(mux->pin_Data(idx), isig->pin(0));
|
||||
|
||||
NetBus tmp (scope, 1);
|
||||
connect(tmp.pin(0), isig->pin(0));
|
||||
stmt->synth_async(des, scope, tmp, tmp);
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
cerr << get_line() << ": sorry: forgot how to implement "
|
||||
<< "NetCase::synth_async" << endl;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NetCondit::synth_async(Design*des, NetScope*scope,
|
||||
|
|
@ -1050,6 +1000,9 @@ void synth2(Design*des)
|
|||
|
||||
/*
|
||||
* $Log: synth2.cc,v $
|
||||
* Revision 1.45 2005/08/27 04:32:08 steve
|
||||
* Handle synthesis of fully packed case statements.
|
||||
*
|
||||
* Revision 1.44 2005/05/15 04:45:50 steve
|
||||
* Debug text.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
#ifdef HAVE_CVS_IDENT
|
||||
#ident "$Id: draw_mux.c,v 1.10 2005/06/17 03:46:52 steve Exp $"
|
||||
#ident "$Id: draw_mux.c,v 1.11 2005/08/27 04:32:08 steve Exp $"
|
||||
#endif
|
||||
|
||||
# include "vvp_priv.h"
|
||||
|
|
@ -48,24 +48,73 @@ static void draw_lpm_mux_ab(ivl_lpm_t net)
|
|||
fprintf(vvp_out, ", C4<>;\n");
|
||||
}
|
||||
|
||||
static void draw_lpm_mux_nest(ivl_lpm_t net)
|
||||
{
|
||||
int idx, level;
|
||||
unsigned width = ivl_lpm_width(net);
|
||||
unsigned swidth = ivl_lpm_selects(net);
|
||||
char*select_input;
|
||||
|
||||
assert(ivl_lpm_size(net) == (1 << swidth));
|
||||
|
||||
select_input = strdup(draw_net_input(ivl_lpm_select(net)));
|
||||
|
||||
fprintf(vvp_out, "L_%p/0s .part %s, 0, 1; Bit 0 of the select\n",
|
||||
net, select_input);
|
||||
|
||||
for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 2) {
|
||||
fprintf(vvp_out, "L_%p/0/%d .functor MUXZ %u",
|
||||
net, idx/2, width);
|
||||
fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx+0)));
|
||||
fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx+1)));
|
||||
fprintf(vvp_out, ", L_%p/0s, C4<>;\n", net);
|
||||
}
|
||||
|
||||
for (level = 1 ; level < swidth-1 ; level += 1) {
|
||||
fprintf(vvp_out, "L_%p/%ds .part %s, %d, 1;\n",
|
||||
net, level, select_input, level);
|
||||
|
||||
for (idx = 0 ; idx < (ivl_lpm_size(net) >> level); idx += 2) {
|
||||
fprintf(vvp_out, "L_%p/%d/%d .functor MUXZ %u",
|
||||
net, width, level,idx);
|
||||
fprintf(vvp_out, ", L_%p/%d/%d", net, level-1, idx/2+0);
|
||||
fprintf(vvp_out, ", L_%p/%d/%d", net, level-1, idx/2+1);
|
||||
fprintf(vvp_out, ", L_%p/%ds", net, level);
|
||||
fprintf(vvp_out, ", C4<>;\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fprintf(vvp_out, "L_%p/%ds .part %s, %d, 1; Bit %d of the select\n",
|
||||
net, swidth-1, select_input, swidth-1, swidth-1);
|
||||
|
||||
|
||||
fprintf(vvp_out, "L_%p .functor MUXZ %u", net, width);
|
||||
fprintf(vvp_out, ", L_%p/%d/0", net, swidth-2);
|
||||
fprintf(vvp_out, ", L_%p/%d/1", net, swidth-2);
|
||||
fprintf(vvp_out, ", L_%p/%ds", net, swidth-1);
|
||||
fprintf(vvp_out, ", C4<>;\n");
|
||||
|
||||
free(select_input);}
|
||||
|
||||
void draw_lpm_mux(ivl_lpm_t net)
|
||||
{
|
||||
|
||||
if ((ivl_lpm_size(net) == 2) && (ivl_lpm_selects(net) == 1)) {
|
||||
draw_lpm_mux_ab(net);
|
||||
return;
|
||||
}
|
||||
# if 0
|
||||
for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1)
|
||||
draw_lpm_mux_bitslice(net, idx);
|
||||
# else
|
||||
fprintf(stderr, "XXXX Forgot how to implement wide muxes.\n");
|
||||
#endif
|
||||
|
||||
/* Here we are at the worst case, we generate a tree of MUXZ
|
||||
devices to handle the arbitrary size. */
|
||||
draw_lpm_mux_nest(net);
|
||||
}
|
||||
|
||||
/*
|
||||
* $Log: draw_mux.c,v $
|
||||
* Revision 1.11 2005/08/27 04:32:08 steve
|
||||
* Handle synthesis of fully packed case statements.
|
||||
*
|
||||
* Revision 1.10 2005/06/17 03:46:52 steve
|
||||
* Make functors know their own width.
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in New Issue