Generate loops to initialize vectors or constants.

This commit is contained in:
steve 2000-12-16 23:55:24 +00:00
parent fe209a6996
commit 4e7f8766fa
1 changed files with 207 additions and 40 deletions

247
t-vvm.cc
View File

@ -17,7 +17,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT) && !defined(macintosh)
#ident "$Id: t-vvm.cc,v 1.195 2000/12/16 16:57:43 steve Exp $"
#ident "$Id: t-vvm.cc,v 1.196 2000/12/16 23:55:24 steve Exp $"
#endif
# include <iostream>
@ -1158,6 +1158,33 @@ void target_vvm::signal(const NetNet*sig)
{
string net_name = mangle(sig->name());
unsigned*ncode_table = new unsigned [sig->pin_count()];
const char*resolution_function = 0;
/* By default, the nexus object uses a resolution
function that is suitable for simulating wire and tri
signals. If the signal is some other sort, the write
a resolution function into the nexus that properly
handles the different semantics. */
switch (sig->type()) {
case NetNet::SUPPLY0:
resolution_function = "vvm_resolution_sup0";
break;
case NetNet::SUPPLY1:
resolution_function = "vvm_resolution_sup1";
break;
case NetNet::TRI0:
resolution_function = "vvm_resolution_tri0";
break;
case NetNet::TRI1:
resolution_function = "vvm_resolution_tri1";
break;
}
/* Scan the signals of the vector, getting an array of all the
nexus numbers. Do any nexus init if necessary. */
for (unsigned idx = 0 ; idx < sig->pin_count() ; idx += 1) {
bool new_nexus_flag = false;
string nexus = sig->pin(idx).nexus()->name();
@ -1168,37 +1195,8 @@ void target_vvm::signal(const NetNet*sig)
new_nexus_flag = true;
}
init_code << " nexus_wire_table[" << ncode <<
"].connect(&" << net_name << ", " << idx << ");" << endl;
ncode_table[idx] = ncode;
/* By default, the nexus object uses a resolution
function that is suitable for simulating wire and tri
signals. If the signal is some other sort, the write
a resolution function into the nexus that properly
handles the different semantics. */
switch (sig->type()) {
case NetNet::SUPPLY0:
init_code << " nexus_wire_table[" << ncode
<< "].resolution_function = vvm_resolution_sup0;"
<< endl;
break;
case NetNet::SUPPLY1:
init_code << " nexus_wire_table[" << ncode
<< "].resolution_function = vvm_resolution_sup1;"
<< endl;
break;
case NetNet::TRI0:
init_code << " nexus_wire_table[" << ncode
<< "].resolution_function = vvm_resolution_tri0;"
<< endl;
break;
case NetNet::TRI1:
init_code << " nexus_wire_table[" << ncode
<< "].resolution_function = vvm_resolution_tri1;"
<< endl;
break;
}
// Propogate the initial value to inputs throughout.
if (new_nexus_flag) {
@ -1207,6 +1205,50 @@ void target_vvm::signal(const NetNet*sig)
}
}
/* Check to see if all the nexus numbers are increasing by one
for each bit of the signal. This is a common case and we
can generate optimal code for the situation. */
bool increasing_flag = true;
for (unsigned idx = 1 ; idx < sig->pin_count() ; idx += 1)
if (ncode_table[idx] != (ncode_table[idx-1] + 1))
increasing_flag = false;
if (increasing_flag) {
unsigned base = ncode_table[0];
init_code << " for (unsigned idx = 0 ; idx < "
<< sig->pin_count() << " ; idx += 1) {" << endl;
init_code << " nexus_wire_table[idx+"<<base
<<"].connect(&" << net_name << ", idx);"
<< endl;
if (resolution_function)
init_code << " nexus_wire_table[idx+" << base
<< "].resolution_function = "
<< resolution_function << ";" << endl;
init_code << " }" << endl;
} else {
for (unsigned idx = 0 ; idx < sig->pin_count() ; idx += 1) {
unsigned ncode = ncode_table[idx];
init_code << " nexus_wire_table[" << ncode
<<"].connect(&" << net_name << ", " << idx << ");"
<< endl;
if (resolution_function)
init_code << " nexus_wire_table[" << ncode
<< "].resolution_function = "
<< resolution_function << ";" << endl;
}
}
delete [] ncode_table;
out << "#define " << net_name << " (signal_table[" <<
signal_counter << "])" << endl;
@ -1229,13 +1271,25 @@ void target_vvm::signal(const NetNet*sig)
}
/* Scan the signals of the vector, passing the initial value
to the inputs of all the connected devices. */
for (unsigned idx = 0 ; idx < sig->pin_count() ; idx += 1) {
/* Look at the initial values of the vector and see if they
can be assigned in a for loop. For this to work, all the
values must be the same. */
verinum::V init = sig->pin(0).nexus()->get_init();
bool uniform_flag = true;
for (unsigned idx = 1 ; idx < sig->pin_count() ; idx += 1)
if (init != sig->pin(idx).nexus()->get_init())
uniform_flag = false;
verinum::V init = sig->pin(idx).nexus()->get_init();
init_code << " " << mangle(sig->name()) << ".init_P("
<< idx << ", ";
if (sig->pin_count() < 2)
uniform_flag = false;
if (uniform_flag) {
/* Generate the short form. Assign all the initial
values of the vector using a for loop. */
init_code << " for (unsigned idx = 0 ; idx < "
<< sig->pin_count() << " ; idx += 1)" << endl;
init_code << " " << mangle(sig->name())<<".init_P(idx, ";
switch (init) {
case verinum::V0:
init_code << "St0";
@ -1251,6 +1305,31 @@ void target_vvm::signal(const NetNet*sig)
break;
}
init_code << ");" << endl;
} else {
/* Scan the signals of the vector, passing the initial
value to the inputs of all the connected devices. */
for (unsigned idx = 0 ; idx < sig->pin_count() ; idx += 1) {
init = sig->pin(idx).nexus()->get_init();
init_code << " " << mangle(sig->name())
<< ".init_P(" << idx << ", ";
switch (init) {
case verinum::V0:
init_code << "St0";
break;
case verinum::V1:
init_code << "St1";
break;
case verinum::Vx:
init_code << "StX";
break;
case verinum::Vz:
init_code << "HiZ";
break;
}
init_code << ");" << endl;
}
}
}
@ -2261,13 +2340,96 @@ void target_vvm::proc_assign_rval(const NetAssign_*lv,
/* We've handled the case of bit selects, so here we know that
we are doing a good ol' assignment to an l-value. So for
the entire width of the l-value, assign constant bit values
to the appropriate nexus. */
to the appropriate nexus.
First make a map of the nexa that are going to receive the
constant value. In the process, check to se if the value is
uniform and the nexa are sequential.
If the nexa are sequential and uniform, write a for loop
that does the assignment. This is an optimization that
reduces the size of the generated C++. */
unsigned*nexus_map = new unsigned[lv->pin_count()];
bool sequential_flag = true;
bool uniform_flag = true;
verinum::V val = off < value.len()
? value.get(off)
: verinum::V0;
unsigned zeros_start = 0;
for (unsigned idx = 0 ; idx < lv->pin_count() ; idx += 1) {
string nexus = lv->pin(idx).nexus()->name();
unsigned ncode = nexus_wire_map[nexus];
nexus_map[idx] = nexus_wire_map[nexus];
verinum::V val = (idx+off) < value.len()
verinum::V tmp = (idx+off) < value.len()
? value.get(idx+off)
: verinum::V0;
if (tmp != verinum::V0)
zeros_start = idx + 1;
if (idx > 0) {
if (nexus_map[idx] != (nexus_map[idx-1] + 1))
sequential_flag = false;
if (tmp != val)
uniform_flag = false;
}
}
if (sequential_flag && uniform_flag && (lv->pin_count() > 1)) {
const char*rval = vvm_val_name(val, Link::STRONG, Link::STRONG);
unsigned base = nexus_map[0];
defn << " for (unsigned idx = 0 ; idx < "
<< lv->pin_count() << " ; idx += 1)" << endl;
defn << " nexus_wire_table[idx+" <<base<< "]"
<< ".reg_assign(" << rval << ");" << endl;
} else if (sequential_flag && (zeros_start < lv->pin_count())) {
/* If the nexa are sequential and the high bits are all
zeros, then we can write simple reg_assign statements
to take care of the low bits, then write a for loop
to fill in all the high zero bits.
This is interesting as it is common to assign small
integers to wide vectors. */
const char*rval;
for (unsigned idx = 0 ; idx < zeros_start ; idx += 1) {
unsigned ncode = nexus_map[idx];
val = (idx+off) < value.len()
? value.get(idx+off)
: verinum::V0;
rval = vvm_val_name(val, Link::STRONG, Link::STRONG);
defn << " nexus_wire_table[" <<ncode<< "]"
<< ".reg_assign(" << rval << ");" << endl;
}
rval = vvm_val_name(verinum::V0, Link::STRONG, Link::STRONG);
unsigned base = nexus_map[zeros_start];
defn << " for (unsigned idx = 0 ; idx < "
<< (lv->pin_count()-zeros_start) << " ; idx += 1)" << endl;
defn << " nexus_wire_table[idx+" <<base<< "]"
<< ".reg_assign(" << rval << ");" << endl;
} else for (unsigned idx = 0 ; idx < lv->pin_count() ; idx += 1) {
unsigned ncode = nexus_map[idx];
val = (idx+off) < value.len()
? value.get(idx+off)
: verinum::V0;
const char*rval = vvm_val_name(val, Link::STRONG, Link::STRONG);
@ -2275,6 +2437,8 @@ void target_vvm::proc_assign_rval(const NetAssign_*lv,
defn << " nexus_wire_table[" <<ncode<< "]"
<< ".reg_assign(" << rval << ");" << endl;
}
delete[]nexus_map;
}
/*
@ -3419,6 +3583,9 @@ extern const struct target tgt_vvm = {
};
/*
* $Log: t-vvm.cc,v $
* Revision 1.196 2000/12/16 23:55:24 steve
* Generate loops to initialize vectors or constants.
*
* Revision 1.195 2000/12/16 16:57:43 steve
* Observe delays in non-blocking assignments (PR#83)
*