diff --git a/design_dump.cc b/design_dump.cc index f642d6d08..3c77aa1e9 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -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); } diff --git a/elab_net.cc b/elab_net.cc index 2e9224483..71670b104 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -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; } diff --git a/elaborate.cc b/elaborate.cc index a07f6b562..84799f46f 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -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: diff --git a/ivl.def b/ivl.def index 4e468cca4..7c46bfbfd 100644 --- a/ivl.def +++ b/ivl.def @@ -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 diff --git a/ivl_target.h b/ivl_target.h index f512058d4..6ab07a9ea 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -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); diff --git a/net_tran.cc b/net_tran.cc index a604b5889..e8088784f 100644 --- a/net_tran.cc +++ b/net_tran.cc @@ -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 (obj); diff --git a/netlist.h b/netlist.h index 1cd02d099..d3dce71dd 100644 --- a/netlist.h +++ b/netlist.h @@ -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); diff --git a/t-dll-api.cc b/t-dll-api.cc index e8354bc99..a9c4c561e 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -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; diff --git a/t-dll.cc b/t-dll.cc index d6174238e..0261a8346 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -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) diff --git a/t-dll.h b/t-dll.h index c444c79c2..63f0a1bb2 100644 --- a/t-dll.h +++ b/t-dll.h @@ -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; diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index 7571fc123..ecec77782 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -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: \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 \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; diff --git a/tgt-stub/switches.c b/tgt-stub/switches.c index 6f6d3c9f9..4728c2ff5 100644 --- a/tgt-stub/switches.c +++ b/tgt-stub/switches.c @@ -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 \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 \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)); } } } diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index 03707be13..bc2bede79 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -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"); } -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; } diff --git a/tgt-vvp/draw_switch.c b/tgt-vvp/draw_switch.c index 93f3ff13a..fa9c67895 100644 --- a/tgt-vvp/draw_switch.c +++ b/tgt-vvp/draw_switch.c @@ -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)); diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 9a18239a2..12cb3d36b 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -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; diff --git a/vvp/compile.h b/vvp/compile.h index cf96c17e7..93daf0f30 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -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); diff --git a/vvp/lexor.lex b/vvp/lexor.lex index db79e4645..c948a12a4 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -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; } diff --git a/vvp/parse.y b/vvp/parse.y index 33af4861f..1bce45a2d 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -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. */ | ';' diff --git a/vvp/vvp_island.cc b/vvp/vvp_island.cc index 9eaca3f34..4a93f3c66 100644 --- a/vvp/vvp_island.cc +++ b/vvp/vvp_island.cc @@ -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 (en->fun); + vvp_island_port*ep = en? dynamic_cast (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&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&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(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<a : ptr->b; + vvp_vector8_t val_other = get_value(net); + + // Temporarily mark as visited. + ptr->flags |= 1< connections; + collect_node(connections, other); + + for (list::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<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) { - listcollection; - collect_connections(collection, vvp_branch_ptr_t(this, 0)); - vvp_vector8_t tmp; - for (list::iterator cur = collection.begin() - ; cur != collection.end() ; cur ++ ) { - vvp_island_port*fun = dynamic_cast((*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 connections; + collect_node(connections, vvp_branch_ptr_t(this, 0)); - for (list::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::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) { - listcollection; - collect_connections(collection, vvp_branch_ptr_t(this, 1)); - vvp_vector8_t tmp; - for (list::iterator cur = collection.begin() - ; cur != collection.end() ; cur ++ ) { - vvp_island_port*fun = dynamic_cast((*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::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::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); diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index 7d3c6e5db..366bfbb7f 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -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