Replace the NetPartSelect:BI with NetTran(VP).

Fold the bi-directional part select into the pass switch (tran) support
so that it can be really bi-directional. This involves adding a new
tranvp device that does part select in tran islands, and reworking the
tran island resolution to handle non-identical nodes. This will be needed
for resistive tran devices anyhow.
This commit is contained in:
Stephen Williams 2008-06-03 11:16:25 -07:00
parent 6e5373c87e
commit 73e2b297df
20 changed files with 403 additions and 294 deletions

View File

@ -109,6 +109,9 @@ ostream& operator << (ostream&o, ivl_switch_type_t val)
case IVL_SW_RTRANIF1:
o << "rtranif1";
break;
case IVL_SW_TRAN_VP:
o << "tran(VP)";
break;
}
return o;
}
@ -518,9 +521,6 @@ void NetPartSelect::dump_node(ostream&o, unsigned ind) const
case PV:
pt = "PV";
break;
case BI:
pt = "BI";
break;
}
o << setw(ind) << "" << "NetPartSelect(" << pt << "): "
<< name();
@ -639,7 +639,13 @@ void NetTaskDef::dump(ostream&o, unsigned ind) const
void NetTran::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << type_ << " " << name()
<< " island " << island << endl;
<< " island " << island;
if (type_ == IVL_SW_TRAN_VP) {
o << " width=" << vector_width()
<< " part=" << part_width()
<< " offset=" << part_offset();
}
o << endl;
dump_node_pins(o, ind+4);
dump_obj_attr(o, ind+4);
}

View File

@ -2375,30 +2375,55 @@ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope,
osig->local_flag(true);
osig->set_line(*this);
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Generating part selects "
<< "to connect input l-value to subexpressions."
<< endl;
if (bidirectional_flag) {
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Generating tran(VP) "
<< "to connect input l-value to subexpressions."
<< endl;
}
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
unsigned wid = nets[idx]->vector_width();
unsigned off = width - wid;
NetTran*ps = new NetTran(scope, scope->local_symbol(),
osig->vector_width(), wid, off);
des->add_node(ps);
ps->set_line(*this);
connect(ps->pin(0), osig->pin(0));
connect(ps->pin(1), nets[idx]->pin(0));
join_island(ps);
ivl_assert(*this, wid <= width);
width -= wid;
}
} else {
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Generating part selects "
<< "to connect input l-value to subexpressions."
<< endl;
}
NetPartSelect::dir_t part_dir = NetPartSelect::VP;
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
unsigned wid = nets[idx]->vector_width();
unsigned off = width - wid;
NetPartSelect*ps = new NetPartSelect(osig, off, wid, part_dir);
des->add_node(ps);
ps->set_line(*this);
connect(ps->pin(1), osig->pin(0));
connect(ps->pin(0), nets[idx]->pin(0));
assert(wid <= width);
width -= wid;
}
assert(width == 0);
}
NetPartSelect::dir_t part_dir = bidirectional_flag
? NetPartSelect::BI
: NetPartSelect::VP;
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
unsigned wid = nets[idx]->vector_width();
unsigned off = width - wid;
NetPartSelect*ps = new NetPartSelect(osig, off, wid, part_dir);
des->add_node(ps);
connect(ps->pin(1), osig->pin(0));
connect(ps->pin(0), nets[idx]->pin(0));
assert(wid <= width);
width -= wid;
}
assert(width == 0);
osig->data_type(nets[0]->data_type());
osig->local_flag(true);
return osig;
@ -2751,11 +2776,6 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
/* If we are processing a tran or inout, then the
partselect is bi-directional. Otherwise, it is a
Part-to-Vector select. */
NetPartSelect::dir_t part_dir;
if (bidirectional_flag)
part_dir = NetPartSelect::BI;
else
part_dir = NetPartSelect::PV;
if (debug_elaborate)
cerr << get_fileline() << ": debug: "
@ -2772,11 +2792,24 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
subsig->local_flag(true);
subsig->set_line(*this);
NetPartSelect*sub = new NetPartSelect(sig, lidx, subnet_wid,
part_dir);
des->add_node(sub);
connect(sub->pin(0), subsig->pin(0));
if (bidirectional_flag) {
// Make a tran(VP)
NetTran*sub = new NetTran(scope, scope->local_symbol(),
sig->vector_width(),
subnet_wid, lidx);
sub->set_line(*this);
des->add_node(sub);
connect(sub->pin(0), sig->pin(0));
connect(sub->pin(1), subsig->pin(0));
join_island(sub);
} else {
NetPartSelect*sub = new NetPartSelect(sig, lidx, subnet_wid,
NetPartSelect::PV);
des->add_node(sub);
sub->set_line(*this);
connect(sub->pin(0), subsig->pin(0));
}
sig = subsig;
}

View File

@ -889,6 +889,32 @@ NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope,
tmp->local_flag(true);
tmp->set_line(*sig);
// Handle the special case of a bi-directional part
// select. Create a NetTran(VP) instead of a uni-directional
// NetPartSelect node.
if (dir == NetNet::PINOUT) {
unsigned wida = sig->vector_width();
unsigned widb = tmp->vector_width();
bool part_b = widb < wida;
NetTran*node = new NetTran(scope, scope->local_symbol(),
part_b? wida : widb,
part_b? widb : wida,
0);
if (part_b) {
connect(node->pin(0), tmp->pin(0));
connect(node->pin(1), sig->pin(0));
} else {
connect(node->pin(0), sig->pin(0));
connect(node->pin(1), tmp->pin(0));
}
node->set_line(*this);
des->add_node(node);
join_island(node);
return tmp;
}
NetPartSelect*node = 0;
switch (dir) {
@ -917,15 +943,7 @@ NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope,
break;
case NetNet::PINOUT:
if (sig->vector_width() > tmp->vector_width()) {
node = new NetPartSelect(sig, 0, tmp->vector_width(),
NetPartSelect::BI);
connect(node->pin(0), tmp->pin(0));
} else {
node = new NetPartSelect(tmp, 0, sig->vector_width(),
NetPartSelect::BI);
connect(node->pin(0), sig->pin(0));
}
ivl_assert(*this, 0);
break;
default:

View File

@ -226,8 +226,11 @@ ivl_switch_enable
ivl_switch_file
ivl_switch_island
ivl_switch_lineno
ivl_switch_offset
ivl_switch_part
ivl_switch_scope
ivl_switch_type
ivl_switch_width
ivl_udp_init
ivl_udp_name

View File

@ -245,7 +245,8 @@ typedef enum ivl_switch_type_e {
IVL_SW_TRANIF1 = 2,
IVL_SW_RTRAN = 3,
IVL_SW_RTRANIF0 = 4,
IVL_SW_RTRANIF1 = 5
IVL_SW_RTRANIF1 = 5,
IVL_SW_TRAN_VP = 6
} ivl_switch_type_t;
/* This is the type of an LPM object. */
@ -265,7 +266,7 @@ typedef enum ivl_lpm_type_e {
IVL_LPM_MOD = 13,
IVL_LPM_MULT = 4,
IVL_LPM_MUX = 5,
IVL_LPM_PART_BI= 28, /* part select: bi-directional (part on 0) */
/* IVL_LPM_PART_BI= 28, / obsolete */
IVL_LPM_PART_VP= 15, /* part select: vector to part */
IVL_LPM_PART_PV= 17, /* part select: part written to vector */
IVL_LPM_POW = 31,
@ -728,8 +729,8 @@ extern unsigned ivl_file_table_size(void);
* Allow the user to test or set a boolean flag associated with the
* island.
*/
extern int ivl_island_flag_set(ivl_island_t net, int flag, int value);
extern int ivl_island_flag_test(ivl_island_t net, int flag);
extern int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value);
extern int ivl_island_flag_test(ivl_island_t net, unsigned flag);
/* LOGIC
* These types and functions support manipulation of logic gates. The
@ -1883,7 +1884,11 @@ extern ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net);
*
* SEMANTIC NOTES
* The a/b ports can be any type, but the types must exactly
* match. The enable must be a scalar.
* match, including vector widths. The enable must be a scalar.
*
* The IVL_SW_TRAN_VP is an exception to the above. In this case,
* the B side may be a different size, and the a side will have a
* a fixed width. The unused bits are padded to Z on the A side.
*/
extern ivl_switch_type_t ivl_switch_type(ivl_switch_t net);
extern ivl_scope_t ivl_switch_scope(ivl_switch_t net);
@ -1893,6 +1898,11 @@ extern ivl_nexus_t ivl_switch_b(ivl_switch_t net);
extern ivl_nexus_t ivl_switch_enable(ivl_switch_t net);
extern ivl_island_t ivl_switch_island(ivl_switch_t net);
/* These are only support for IVL_SW_TRAN_VP switches. */
extern unsigned ivl_switch_width(ivl_switch_t net);
extern unsigned ivl_switch_part(ivl_switch_t net);
extern unsigned ivl_switch_offset(ivl_switch_t net);
/* Not implemented yet
extern unsigned ivl_switch_attr_cnt(ivl_switch_t net);
extern ivl_attribute_t ivl_switch_attr_val(ivl_switch_t net, unsigned idx);

View File

@ -43,7 +43,7 @@ static bool has_enable(ivl_switch_type_t tt)
}
NetTran::NetTran(NetScope*scope, perm_string n, ivl_switch_type_t tt)
: NetNode(scope, n, has_enable(tt)? 3 : 2), type_(tt)
: NetNode(scope, n, has_enable(tt)? 3 : 2), type_(tt)
{
pin(0).set_dir(Link::PASSIVE); pin(0).set_name(perm_string::literal("A"), 0);
pin(1).set_dir(Link::PASSIVE); pin(1).set_name(perm_string::literal("B"), 0);
@ -53,10 +53,32 @@ NetTran::NetTran(NetScope*scope, perm_string n, ivl_switch_type_t tt)
}
}
NetTran::NetTran(NetScope*scope, perm_string n, unsigned wid, unsigned part, unsigned off)
: NetNode(scope, n, 2), type_(IVL_SW_TRAN_VP), wid_(wid), part_(part), off_(off)
{
pin(0).set_dir(Link::PASSIVE); pin(0).set_name(perm_string::literal("A"), 0);
pin(1).set_dir(Link::PASSIVE); pin(1).set_name(perm_string::literal("B"), 0);
}
NetTran::~NetTran()
{
}
unsigned NetTran::vector_width() const
{
return wid_;
}
unsigned NetTran::part_width() const
{
return part_;
}
unsigned NetTran::part_offset() const
{
return off_;
}
void join_island(NetObj*obj)
{
IslandBranch*branch = dynamic_cast<IslandBranch*> (obj);

View File

@ -1379,16 +1379,28 @@ class NetSysFunc : public NetNode {
class NetTran : public NetNode, public IslandBranch {
public:
// Tran devices other then TRAN_VP
NetTran(NetScope*scope, perm_string n, ivl_switch_type_t type);
// Create a TRAN_VP
NetTran(NetScope*scope, perm_string n, unsigned wid,
unsigned part, unsigned off);
~NetTran();
ivl_switch_type_t type() const { return type_; }
// These are only used for IVL_SW_TRAN_PV
unsigned vector_width() const;
unsigned part_width() const;
unsigned part_offset() const;
virtual void dump_node(ostream&, unsigned ind) const;
virtual bool emit_node(struct target_t*) const;
private:
ivl_switch_type_t type_;
unsigned wid_;
unsigned part_;
unsigned off_;
};
/* =========
@ -1605,8 +1617,8 @@ class NetECRealParam : public NetECReal {
* selector as the input.
*
* The NetPartSelect can be output from the signal (i.e. reading a
* part), input into the signal, or bi-directional. The DIR method
* gives the type of the node.
* part) or input into the signal. The DIR method gives the type of
* the node.
*
* VP (Vector-to-Part)
* Output pin 0 is the part select, and input pin 1 is connected to
@ -1616,10 +1628,6 @@ class NetECRealParam : public NetECReal {
* Output pin 1 is connected to the NetNet, and input pin 0 is the
* part select. In this case, the node is driving the NetNet.
*
* BI (BI-directional)
* Pin 0 is the part select and pin 1 is connected to the NetNet, but
* the ports are intended to be bi-directional.
*
* Note that whatever the direction that data is intended to flow,
* pin-0 is the part select and pin-1 is connected to the NetNet.
*/
@ -1627,7 +1635,7 @@ class NetPartSelect : public NetNode {
public:
// enum for the device direction
enum dir_t { VP, PV, BI };
enum dir_t { VP, PV};
explicit NetPartSelect(NetNet*sig,
unsigned off, unsigned wid, dir_t dir);

View File

@ -555,7 +555,7 @@ extern "C" unsigned ivl_file_table_size()
return fn_vector.size();
}
extern "C" int ivl_island_flag_set(ivl_island_t net, int flag, int value)
extern "C" int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value)
{
if (flag >= net->flags.size()) {
if (value == 0)
@ -569,7 +569,7 @@ extern "C" int ivl_island_flag_set(ivl_island_t net, int flag, int value)
return old_flag;
}
extern "C" int ivl_island_flag_test(ivl_island_t net, int flag)
extern "C" int ivl_island_flag_test(ivl_island_t net, unsigned flag)
{
if (flag >= net->flags.size())
return 0;
@ -804,7 +804,6 @@ extern "C" unsigned ivl_lpm_base(ivl_lpm_t net)
switch (net->type) {
case IVL_LPM_PART_VP:
case IVL_LPM_PART_PV:
case IVL_LPM_PART_BI:
return net->u_.part.base;
default:
assert(0);
@ -940,7 +939,6 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx)
case IVL_LPM_PART_VP:
case IVL_LPM_PART_PV:
case IVL_LPM_PART_BI:
assert(idx <= 1);
if (idx == 0)
return net->u_.part.a;
@ -1083,7 +1081,6 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx)
case IVL_LPM_PART_VP:
case IVL_LPM_PART_PV:
case IVL_LPM_PART_BI:
assert(idx == 0);
return net->u_.part.q;
@ -1179,7 +1176,6 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net)
return 0;
case IVL_LPM_PART_VP:
case IVL_LPM_PART_PV:
case IVL_LPM_PART_BI:
return net->u_.part.signed_flag;
case IVL_LPM_REPEAT:
return 0;
@ -2218,6 +2214,21 @@ extern "C" ivl_nexus_t ivl_switch_enable(ivl_switch_t net)
return net->pins[2];
}
extern "C" unsigned ivl_switch_width(ivl_switch_t net)
{
return net->width;
}
extern "C" unsigned ivl_switch_part(ivl_switch_t net)
{
return net->part;
}
extern "C" unsigned ivl_switch_offset(ivl_switch_t net)
{
return net->offset;
}
extern "C" const char* ivl_switch_file(ivl_switch_t net)
{
return net->file;

View File

@ -1037,6 +1037,9 @@ bool dll_target::tran(const NetTran*net)
{
struct ivl_switch_s*obj = new struct ivl_switch_s;
obj->type = net->type();
obj->width = 0;
obj->part = 0;
obj->offset = 0;
obj->name = net->name();
obj->scope = find_scope(des_, net->scope());
obj->island = net->island;
@ -1065,6 +1068,12 @@ bool dll_target::tran(const NetTran*net)
obj->pins[2] = 0;
}
if (obj->type == IVL_SW_TRAN_VP) {
obj->width = net->vector_width();
obj->part = net->part_width();
obj->offset= net->part_offset();
}
obj->file = net->get_file();
obj->lineno = net->get_lineno();
@ -1996,9 +2005,6 @@ bool dll_target::part_select(const NetPartSelect*net)
case NetPartSelect::PV:
obj->type = IVL_LPM_PART_PV;
break;
case NetPartSelect::BI:
obj->type = IVL_LPM_PART_BI;
break;
}
obj->name = net->name(); // NetPartSelect names are permallocated.
assert(net->scope());
@ -2052,35 +2058,12 @@ bool dll_target::part_select(const NetPartSelect*net)
obj->u_.part.a = nex->t_cookie();
break;
case IVL_LPM_PART_BI:
/* For now, handle this exactly the same as a PV */
/* NetPartSelect:pin(0) is the output pin. */
nex = net->pin(0).nexus();
assert(nex->t_cookie());
obj->u_.part.q = nex->t_cookie();
/* NetPartSelect:pin(1) is the input pin. */
nex = net->pin(1).nexus();
assert(nex->t_cookie());
obj->u_.part.a = nex->t_cookie();
break;
default:
assert(0);
}
nexus_lpm_add(obj->u_.part.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG);
/* If the device is a PART_BI, then the "input" is also a
strength aware output, so attach it to the nexus with
strong driver. */
if (obj->type == IVL_LPM_PART_BI)
nexus_lpm_add(obj->u_.part.a, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG);
else
nexus_lpm_add(obj->u_.part.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
nexus_lpm_add(obj->u_.part.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
/* The select input is optional. */
if (obj->u_.part.s)

View File

@ -451,6 +451,9 @@ struct ivl_net_logic_s {
struct ivl_switch_s {
ivl_switch_type_t type;
unsigned width;
unsigned part;
unsigned offset;
perm_string name;
ivl_scope_t scope;

View File

@ -555,38 +555,6 @@ static void show_lpm_part(ivl_lpm_t net)
}
}
static void show_lpm_part_bi(ivl_lpm_t net)
{
unsigned width = ivl_lpm_width(net);
unsigned base = ivl_lpm_base(net);
ivl_nexus_t port_p = ivl_lpm_q(net,0);
ivl_nexus_t port_v = ivl_lpm_data(net,0);
fprintf(out, " LPM_PART_BI %s: <width=%u, base=%u, signed=%d>\n",
ivl_lpm_basename(net), width, base, ivl_lpm_signed(net));
fprintf(out, " P: %s\n", ivl_nexus_name(port_p));
fprintf(out, " V: %s <width=%u>\n", ivl_nexus_name(port_v),
width_of_nexus(port_v));
/* The data(0) port must be large enough for the part select. */
if (width_of_nexus(ivl_lpm_data(net,0)) < (width+base)) {
fprintf(out, " ERROR: Part select is out of range."
" Data nexus width=%u, width+base=%u\n",
width_of_nexus(ivl_lpm_data(net,0)), width+base);
stub_errors += 1;
}
/* The Q vector must be exactly the width of the part select. */
if (width_of_nexus(ivl_lpm_q(net,0)) != width) {
fprintf(out, " ERROR: Part select input mismatch."
" Nexus width=%u, expect width=%u\n",
width_of_nexus(ivl_lpm_q(net,0)), width);
stub_errors += 1;
}
}
/*
* The reduction operators have similar characteristics and are
* displayed here.
@ -905,11 +873,6 @@ static void show_lpm(ivl_lpm_t net)
show_lpm_part(net);
break;
/* The BI part select is slightly special. */
case IVL_LPM_PART_BI:
show_lpm_part_bi(net);
break;
case IVL_LPM_REPEAT:
show_lpm_repeat(net);
break;

View File

@ -52,18 +52,23 @@ void show_switch(ivl_switch_t net)
fprintf(out, " rtranif1 %s", name);
has_enable = 1;
break;
case IVL_SW_TRAN_VP:
fprintf(out, " tran(PV wid=%u, part=%u, off=%u) %s",
ivl_switch_width(net), ivl_switch_part(net),
ivl_switch_offset(net), name);
break;
}
fprintf(out, " island=%p\n", ivl_switch_island(net));
ivl_nexus_t nex = ivl_switch_a(net);
const char*nex_name = nex? ivl_nexus_name(nex) : "";
ivl_variable_type_t nex_type_a = nex? type_of_nexus(nex) : IVL_VT_NO_TYPE;
ivl_nexus_t nexa = ivl_switch_a(net);
const char*nex_name = nexa? ivl_nexus_name(nexa) : "";
ivl_variable_type_t nex_type_a = nexa? type_of_nexus(nexa) : IVL_VT_NO_TYPE;
fprintf(out, " A: %s <type=%s>\n", nex_name, data_type_string(nex_type_a));
nex = ivl_switch_b(net);
nex_name = nex? ivl_nexus_name(nex) : "";
ivl_variable_type_t nex_type_b = nex? type_of_nexus(nex) : IVL_VT_NO_TYPE;
ivl_nexus_t nexb = ivl_switch_b(net);
nex_name = nexb? ivl_nexus_name(nexb) : "";
ivl_variable_type_t nex_type_b = nexb? type_of_nexus(nexb) : IVL_VT_NO_TYPE;
fprintf(out, " B: %s <type=%s>\n", nex_name, data_type_string(nex_type_b));
/* The A/B pins of the switch must be present, and must match. */
@ -80,13 +85,40 @@ void show_switch(ivl_switch_t net)
stub_errors += 1;
}
if (ivl_switch_type(net) == IVL_SW_TRAN_VP) {
/* The TRAN_VP nodes are special in that the specific
width matters for each port and should be exactly
right for both. */
if (width_of_nexus(nexa) != ivl_switch_width(net)) {
fprintf(out, " A: ERROR: part vector nexus "
"width=%u, expecting width=%u\n",
width_of_nexus(nexa), ivl_switch_width(net));
stub_errors += 1;
}
if (width_of_nexus(nexb) != ivl_switch_part(net)) {
fprintf(out, " B: ERROR: part select nexus "
"width=%u, expecting width=%u\n",
width_of_nexus(nexb), ivl_switch_part(net));
stub_errors += 1;
}
} else {
/* All other TRAN nodes will have matching vector
widths, but the actual value doesn't matter. */
if (width_of_nexus(nexa) != width_of_nexus(nexb)) {
fprintf(out, " A/B: ERROR: Width of ports don't match"
": A=%u, B=%u\n",
width_of_nexus(nexa), width_of_nexus(nexb));
stub_errors += 1;
}
}
if (has_enable) {
nex = ivl_switch_enable(net);
nex_name = nex? ivl_nexus_name(nex) : "";
ivl_nexus_t nexe = ivl_switch_enable(net);
nex_name = nexe? ivl_nexus_name(nexe) : "";
fprintf(out, " E: %s\n", nex_name);
if (width_of_nexus(nex) != 1) {
if (width_of_nexus(nexe) != 1) {
fprintf(out, " E: ERROR: Nexus width is %u\n",
width_of_nexus(nex));
width_of_nexus(nexe));
}
}
}

View File

@ -421,17 +421,6 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
}
break;
case IVL_LPM_PART_BI:
if (ivl_lpm_q(lpm, 0) == nex) {
char tmp[128];
snprintf(tmp, sizeof tmp, "L_%p/P", lpm);
return strdup(tmp);
} else if (ivl_lpm_data(lpm,0) == nex) {
char tmp[128];
snprintf(tmp, sizeof tmp, "L_%p/V", lpm);
return strdup(tmp);
}
break;
}
fprintf(stderr, "internal error: no input to nexus %s\n",
@ -440,7 +429,8 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
return strdup("C<z>");
}
static char* draw_island_port(ivl_island_t island, ivl_nexus_t nex, const char*src)
static char* draw_island_port(ivl_island_t island,
ivl_nexus_t nex, const char*src)
{
char result[64];
if (ivl_island_flag_test(island,0) == 0) {
@ -450,6 +440,7 @@ static char* draw_island_port(ivl_island_t island, ivl_nexus_t nex, const char*s
fprintf(vvp_out, "p%p .port I%p, %s;\n", nex, island, src);
snprintf(result, sizeof result, "p%p", nex);
return strdup(result);
}
@ -517,13 +508,14 @@ char* draw_net_input_x(ivl_nexus_t nex,
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
ivl_lpm_t lpm_tmp;
ivl_switch_t sw;
ivl_switch_t sw = 0;
ivl_nexus_ptr_t nptr = ivl_nexus_ptr(nex, idx);
/* If this object is part of an island, then we'll be
making a port. Save the island cookie. */
if ( (sw = ivl_nexus_ptr_switch(nptr)) )
if ( (sw = ivl_nexus_ptr_switch(nptr)) ) {
island = ivl_switch_island(sw);
}
/* If we are supposed to skip LPM_PART_BI data pins,
check that this driver is that. */
@ -589,6 +581,11 @@ char* draw_net_input_x(ivl_nexus_t nex,
}
*tmp++ = '>';
*tmp = 0;
if (island) {
char*tmp = draw_island_port(island, nex, nex_private);
free(nex_private);
nex_private = tmp;
}
return nex_private;
}

View File

@ -53,14 +53,19 @@ void draw_switch_in_scope(ivl_switch_t sw)
switch (ivl_switch_type(sw)) {
case IVL_SW_TRAN:
fprintf(vvp_out, " .tran ");
fprintf(vvp_out, " .tran");
break;
case IVL_SW_TRANIF0:
fprintf(vvp_out, " .tranif0 ");
fprintf(vvp_out, " .tranif0");
break;
case IVL_SW_TRANIF1:
fprintf(vvp_out, " .tranif1 ");
fprintf(vvp_out, " .tranif1");
break;
case IVL_SW_TRAN_VP:
fprintf(vvp_out, " .tranvp %u %u %u,",
ivl_switch_width(sw), ivl_switch_part(sw), ivl_switch_offset(sw));
break;
default:
fprintf(stderr, "%s:%u: sorry: vvp target does not support switch modeling.\n",
ivl_switch_file(sw), ivl_switch_lineno(sw));

View File

@ -1611,94 +1611,6 @@ static void draw_lpm_part_pv(ivl_lpm_t net)
fprintf(vvp_out, ", %u, %u, %u;\n", base, width, signal_width);
}
/*
* Handle the drawing of a bi-directional part select. The two ports
* are simultaneously input and output. A simple minded connect of the
* input to the output causes a functor cycle which will lock into an
* X value, so something special is needed.
*
* NOTE: The inputs of the tran device at this point need to be from
* all the drivers of the nexus *except* the tran itself. This
* function will draw three labels that can be linked:
*
* The ivl_lpm_q of a part(bi) may be a smaller vector then the
* ivl_lpm_data, the tran acts like a forward part select in that
* way.
*
* The device creates these nodes:
*
* - L_%p/i
* This is the Q port of the tran resolved and padded to the maximum
* width of the tran. The tran itself is not included in the
* resolution of this port.
*
* - L_%p/V
* This is the Q and D parts resolved together, still without the tran
* driving anything.
*
* - L_%p/P
* This is the /V node part-selected back to the dimensions of the Q
* side.
*/
static void draw_lpm_part_bi(ivl_lpm_t net)
{
unsigned width = ivl_lpm_width(net);
unsigned base = ivl_lpm_base(net);
unsigned signal_width = width_of_nexus(ivl_lpm_data(net,0));
unsigned idx;
ivl_nexus_t nex;
ivl_nexus_ptr_t ptr = 0;
char*p_str;
char*v_str;
/* It seems implausible that the two inputs of a tran will be
connected together. So assert that this is so to simplify
the code to look for the nexus_ptr_t objects. */
assert(ivl_lpm_q(net,0) != ivl_lpm_data(net,0));
nex = ivl_lpm_q(net,0);
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
ptr = ivl_nexus_ptr(nex, idx);
if (ivl_nexus_ptr_lpm(ptr) == net)
break;
}
assert(ptr != 0);
p_str = draw_net_input_x(nex, ptr, 0, 0);
nex = ivl_lpm_data(net,0);
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
ptr = ivl_nexus_ptr(nex, idx);
if (ivl_nexus_ptr_lpm(ptr) == net)
break;
}
v_str = draw_net_input_x(nex, ptr, OMIT_PART_BI_DATA, 0);
/* Pad the part-sized input out to a common width...
The /i label is the Q side of the tran, resolved except for
the tran itself and padded (with z) to the larger width. */
fprintf(vvp_out, "L_%p/i .part/pv %s, %u, %u, %u;\n",
net, p_str, base, width, signal_width);
/* Resolve together the two halves of the tran...
The /V label is the ports of the tran (now the same width)
resolved together. Neither input to this resolver includes
the tran itself. */
fprintf(vvp_out, "L_%p/V .resolv tri, L_%p/i, %s;\n",
net, net, v_str);
/* The full-width side is created by the tran device, all we
have left to to is take a part select of that for the
smaller output, and this becomes the part select output of
the BI device. */
fprintf(vvp_out, "L_%p/P .part L_%p/V, %u, %u;\n", net,
net, base, width);
free(p_str);
free(v_str);
}
/*
* Draw unary reduction devices.
*/
@ -1745,10 +1657,6 @@ static void draw_lpm_in_scope(ivl_lpm_t net)
draw_lpm_array(net);
return;
case IVL_LPM_PART_BI:
draw_lpm_part_bi(net);
return;
case IVL_LPM_PART_VP:
draw_lpm_part(net);
return;

View File

@ -460,6 +460,8 @@ extern void compile_island_export(char*label, char*island);
extern void compile_island_tranif(int sense, char*island,
char*ba, char*bb, char*src);
extern void compile_island_tranvp(char*island, char*ba, char*bb,
unsigned width, unsigned part, unsigned off);
extern void compile_island_cleanup(void);

View File

@ -160,6 +160,7 @@
".tran" { return K_TRAN; }
".tranif0" { return K_TRANIF0; }
".tranif1" { return K_TRANIF1; }
".tranvp" { return K_TRANVP; }
".ufunc" { return K_UFUNC; }
".var" { return K_VAR; }
".var/real" { return K_VAR_R; }

View File

@ -81,7 +81,7 @@ static struct __vpiModPath*modpath_dst = 0;
%token K_PART_V K_PORT K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR
%token K_REDUCE_NAND K_REDUCE_NOR K_REDUCE_XNOR K_REPEAT
%token K_RESOLV K_SCOPE K_SFUNC K_SHIFTL K_SHIFTR K_SHIFTRS
%token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_UFUNC
%token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP K_UFUNC
%token K_UDP K_UDP_C K_UDP_S
%token K_VAR K_VAR_S K_VAR_I K_VAR_R K_vpi_call K_vpi_func K_vpi_func_r
%token K_disable K_fork
@ -697,6 +697,9 @@ statement
| K_TRANIF1 T_SYMBOL ',' T_SYMBOL T_SYMBOL ',' T_SYMBOL ';'
{ compile_island_tranif(1, $2, $4, $5, $7); }
| K_TRANVP T_NUMBER T_NUMBER T_NUMBER ',' T_SYMBOL ',' T_SYMBOL T_SYMBOL ';'
{ compile_island_tranvp($6, $8, $9, $2, $3, $4); }
/* Oh and by the way, empty statements are OK as well. */
| ';'

View File

@ -166,6 +166,7 @@ struct vvp_island_branch {
bool enabled_flag;
vvp_net_t*en;
int flags;
unsigned width, part, offset;
};
/*
@ -321,7 +322,7 @@ bool vvp_island_branch::run_test_enabled()
{
flags = 0;
vvp_island_port*ep = dynamic_cast<vvp_island_port*> (en->fun);
vvp_island_port*ep = en? dynamic_cast<vvp_island_port*> (en->fun) : 0;
// If there is no ep port (no "enabled" input) then this is a
// tran branch. Assume it is always enabled.
@ -343,63 +344,138 @@ bool vvp_island_branch::run_test_enabled()
return true;
}
void collect_connections(list<vvp_net_t*>&connections, vvp_branch_ptr_t cur)
static vvp_branch_ptr_t next(vvp_branch_ptr_t cur)
{
vvp_island_branch*ptr = cur.ptr();
unsigned ab = cur.port();
unsigned other_ab = ab^1;
int ab_mask = 1 << ab;
if ( (ptr->flags&ab_mask) != 0)
return;
ptr->flags |= ab_mask;
connections.push_back( ab? ptr->b : ptr->a );
if (ptr->enabled_flag)
collect_connections(connections, vvp_branch_ptr_t(ptr, other_ab));
collect_connections(connections, ptr->link[ab]);
return ptr->link[ab];
}
static void collect_node(list<vvp_branch_ptr_t>&conn, vvp_branch_ptr_t cur)
{
conn .push_back(cur);
for (vvp_branch_ptr_t idx = next(cur) ; idx != cur ; idx = next(idx))
conn.push_back(idx);
}
static vvp_vector8_t get_value(vvp_net_t*net)
{
vvp_island_port*fun = dynamic_cast<vvp_island_port*>(net->fun);
return fun->invalue;
}
static vvp_vector8_t get_value_from_branch(vvp_branch_ptr_t cur)
{
vvp_island_branch*ptr = cur.ptr();
unsigned ab = cur.port();
unsigned ab_other = ab^1;
// If the branch link is disabled, return nil.
if (ptr->enabled_flag == false)
return vvp_vector8_t();
// If the branch other side is already visited, return nil.
if (ptr->flags & (1<<ab_other))
return vvp_vector8_t();
vvp_branch_ptr_t other (ptr, ab_other);
// Other side net, and port value.
vvp_net_t*net = ab? ptr->a : ptr->b;
vvp_vector8_t val_other = get_value(net);
// Temporarily mark as visited.
ptr->flags |= 1<<ab_other;
// recurse
list<vvp_branch_ptr_t> connections;
collect_node(connections, other);
for (list<vvp_branch_ptr_t>::iterator idx = connections.begin()
; idx != connections.end() ; idx ++ ) {
vvp_vector8_t tmp = get_value_from_branch(*idx);
if (val_other.size() == 0)
val_other = tmp;
else if (tmp.size() != 0)
val_other = resolve(val_other, tmp);
}
// Remove visited flag
ptr->flags &= ~(1<<ab_other);
if (val_other.size() == 0)
return val_other;
if (ptr->width) {
if (ab == 0) {
val_other = part_expand(val_other, ptr->width, ptr->offset);
} else {
val_other = val_other.subvalue(ptr->offset, ptr->part);
}
}
return val_other;
}
void vvp_island_branch::run_resolution()
{
if ( (flags&1) == 0) {
list<vvp_net_t*>collection;
collect_connections(collection, vvp_branch_ptr_t(this, 0));
vvp_vector8_t tmp;
for (list<vvp_net_t*>::iterator cur = collection.begin()
; cur != collection.end() ; cur ++ ) {
vvp_island_port*fun = dynamic_cast<vvp_island_port*>((*cur)->fun);
if (tmp.size() == 0)
tmp = fun->invalue;
else if (fun->invalue.size() != 0)
tmp = resolve(tmp, fun->invalue);
}
// Collect all the branch endpoints that are joined to my A
// side.
list<vvp_branch_ptr_t> connections;
collect_node(connections, vvp_branch_ptr_t(this, 0));
for (list<vvp_net_t*>::iterator cur = collection.begin()
; cur != collection.end() ; cur ++ )
vvp_send_vec8((*cur)->out, tmp);
// Mark my A side as done. Do this early to prevent recursing
// back.
flags |= 1;
// Prime the resolution with the port value for this node.
vvp_vector8_t val = get_value(a);
// Now scan the other sides of all the branches connected to
// my A side. The get_value_from_branch() will recurse as
// necessary to depth-first walk the graph.
for (list<vvp_branch_ptr_t>::iterator idx = connections.begin()
; idx != connections.end() ; idx ++ ) {
vvp_vector8_t tmp = get_value_from_branch(*idx);
if (val.size() == 0)
val = tmp;
else if (tmp.size() != 0)
val = resolve(val, tmp);
}
if ( (flags&2) == 0) {
list<vvp_net_t*>collection;
collect_connections(collection, vvp_branch_ptr_t(this, 1));
vvp_vector8_t tmp;
for (list<vvp_net_t*>::iterator cur = collection.begin()
; cur != collection.end() ; cur ++ ) {
vvp_island_port*fun = dynamic_cast<vvp_island_port*>((*cur)->fun);
if (tmp.size() == 0)
tmp = fun->invalue;
else if (fun->invalue.size() != 0)
tmp = resolve(tmp, fun->invalue);
}
// A side is done.
vvp_send_vec8(a->out, val);
for (list<vvp_net_t*>::iterator cur = collection.begin()
; cur != collection.end() ; cur ++ )
vvp_send_vec8((*cur)->out, tmp);
flags &= ~1;
// If this is a connected branch without a part select, then
// we know from the start that the other side has the same
// value as this. Mark it and finish.
if (enabled_flag && width==0) {
vvp_send_vec8(b->out, val);
return;
}
// Repeat the above for the B side.
connections.clear();
collect_node(connections, vvp_branch_ptr_t(this, 1));
flags |= 2;
val = get_value(b);
for (list<vvp_branch_ptr_t>::iterator idx = connections.begin()
; idx != connections.end() ; idx ++ ) {
vvp_vector8_t tmp = get_value_from_branch(*idx);
if (val.size() == 0)
val = tmp;
else if (tmp.size() != 0)
val = resolve(val, tmp);
}
flags &= ~2;
vvp_send_vec8(b->out, val);
}
/* **** COMPILE/LINK SUPPORT **** */
@ -502,6 +578,31 @@ void compile_island_tranif(int sense, char*island, char*pa, char*pb, char*pe)
free(pe);
}
br->width = 0;
br->part = 0;
br->offset = 0;
use_island->add_branch(br, pa, pb);
free(pa);
free(pb);
}
void compile_island_tranvp(char*island, char*pa, char*pb,
unsigned wid, unsigned par, unsigned off)
{
assert(island_table);
vvp_island*use_island = island_table->sym_get_value(island);
assert(use_island);
free(island);
vvp_island_branch*br = new vvp_island_branch;
br->active_high = false;
br->en = 0;
br->width = wid;
br->part = par;
br->offset = off;
use_island->add_branch(br, pa, pb);
free(pa);

View File

@ -556,7 +556,7 @@ class vvp_vector8_t {
unsigned size() const { return size_; }
vvp_scalar_t value(unsigned idx) const;
vvp_vector8_t subvalue(unsigned idx, unsigned size) const;
vvp_vector8_t subvalue(unsigned adr, unsigned width) const;
void set_bit(unsigned idx, vvp_scalar_t val);
// Test that the vectors are exactly equal