From d5fb0f4344f7874dcad8e9b8cda3f9a03fac2931 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 13 Jul 2014 18:09:13 -0700 Subject: [PATCH] Handle some tricky conditions assignments to parts. When for example assigning to foo[] within a contitional, and doing synthesis, we need to create a NetSubstitute device to manage the l-value bit selects. --- design_dump.cc | 15 +++ emit.cc | 5 + ivl_target.h | 7 +- net_nex_output.cc | 23 +++-- netlist.cc | 14 +++ netlist.h | 34 +++++++ synth2.cc | 200 +++++++++++++++++++++++++++++++------- t-dll-api.cc | 13 +++ t-dll.cc | 26 +++++ t-dll.h | 6 ++ target.cc | 7 ++ target.h | 1 + tgt-stub/stub.c | 35 +++++++ tgt-vvp/Makefile.in | 2 +- tgt-vvp/draw_net_input.c | 1 + tgt-vvp/draw_substitute.c | 32 ++++++ tgt-vvp/vvp_priv.h | 1 + tgt-vvp/vvp_scope.c | 4 + vvp/Makefile.in | 4 +- vvp/README.txt | 16 +++ vvp/compile.h | 4 + vvp/lexor.lex | 1 + vvp/parse.y | 5 + vvp/substitute.cc | 94 ++++++++++++++++++ 24 files changed, 499 insertions(+), 51 deletions(-) create mode 100644 tgt-vvp/draw_substitute.c create mode 100644 vvp/substitute.cc diff --git a/design_dump.cc b/design_dump.cc index 75fdb1476..a280ffffb 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -778,6 +778,21 @@ void NetPartSelect::dump_node(ostream&o, unsigned ind) const dump_obj_attr(o, ind+4); } +void NetSubstitute::dump_node(ostream&fd, unsigned ind) const +{ + fd << setw(ind) << "" << "NetSubstitute: " + << name(); + if (rise_time()) + fd << " #(" << *rise_time() + << "," << *fall_time() + << "," << *decay_time() << ")"; + else + fd << " #(.,.,.)"; + fd << " width=" << wid_ << " base=" << off_ <sign_extend(this); } +bool NetSubstitute::emit_node(struct target_t*tgt) const +{ + return tgt->substitute(this); +} + bool NetUReduce::emit_node(struct target_t*tgt) const { return tgt->ureduce(this); diff --git a/ivl_target.h b/ivl_target.h index 56f5fc476..234123208 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -328,6 +328,7 @@ typedef enum ivl_lpm_type_e { IVL_LPM_SHIFTR = 7, IVL_LPM_SIGN_EXT=27, IVL_LPM_SUB = 8, + IVL_LPM_SUBSTITUTE=39, /* IVL_LPM_RAM = 9, / obsolete */ IVL_LPM_UFUNC = 14 } ivl_lpm_type_t; @@ -1409,7 +1410,7 @@ extern ivl_nexus_t ivl_lpm_sync_set(ivl_lpm_t net); extern ivl_expr_t ivl_lpm_sset_value(ivl_lpm_t net); /* IVL_LPM_ARRAY */ extern ivl_signal_t ivl_lpm_array(ivl_lpm_t net); - /* IVL_LPM_PART */ + /* IVL_LPM_PART IVL_LPM_SUBSTITUTE */ extern unsigned ivl_lpm_base(ivl_lpm_t net); /* IVL_LPM_FF */ extern ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net); @@ -1419,14 +1420,14 @@ extern ivl_scope_t ivl_lpm_define(ivl_lpm_t net); extern ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net); /* IVL_LPM_ADD IVL_LPM_CONCAT IVL_LPM_FF IVL_LPM_PART IVL_LPM_MULT IVL_LPM_MUX IVL_LPM_POW IVL_LPM_SHIFTL IVL_LPM_SHIFTR IVL_LPM_SUB - IVL_LPM_UFUNC */ + IVL_LPM_UFUNC IVL_LPM_SUBSTITUTE */ extern ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx); /* IVL_LPM_ADD IVL_LPM_MULT IVL_LPM_POW IVL_LPM_SUB IVL_LPM_CMP_EQ IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE */ extern ivl_nexus_t ivl_lpm_datab(ivl_lpm_t net, unsigned idx); /* IVL_LPM_ADD IVL_LPM_FF IVL_LPM_MULT IVL_LPM_PART IVL_LPM_POW IVL_LPM_SUB IVL_LPM_UFUNC IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX - IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE */ + IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE IVL_LPM_SUBSTITUTE */ extern ivl_nexus_t ivl_lpm_q(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive0(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive1(ivl_lpm_t net); diff --git a/net_nex_output.cc b/net_nex_output.cc index a6cc52986..194947955 100644 --- a/net_nex_output.cc +++ b/net_nex_output.cc @@ -60,19 +60,18 @@ void NetAssign_::nex_output(NexusSet&out) } Nexus*nex = sig_->pin(use_word).nexus(); if (base_) { - long tmp = 0; - bool flag = eval_as_long(tmp, base_); - if (!flag) { - // Unable to evaluate the bit/part select of - // the l-value, so this is a mux. Pretty - // sure I don't know how to handle this yet - // in synthesis, so punt for now. - use_base = 0; - use_wid = nex->vector_width(); - } else { - use_base = tmp; - } + // Unable to evaluate the bit/part select of + // the l-value, so this is a mux. Pretty + // sure I don't know how to handle this yet + // in synthesis, so punt for now. + + // Even with constant bit/part select, we want to + // return the entire signal as an output. The + // context will need to sort out which bits are + // actually assigned. + use_base = 0; + use_wid = nex->vector_width(); } out.add(nex, use_base, use_wid); } diff --git a/netlist.cc b/netlist.cc index 0bc922643..5bd211bd7 100644 --- a/netlist.cc +++ b/netlist.cc @@ -989,6 +989,20 @@ unsigned NetPartSelect::base() const return off_; } +NetSubstitute::NetSubstitute(NetNet*sig, NetNet*sub, unsigned wid, unsigned off) +: NetNode(sig->scope(), sig->scope()->local_symbol(), 3), wid_(wid), off_(off) +{ + pin(0).set_dir(Link::OUTPUT); + pin(1).set_dir(Link::INPUT); + pin(2).set_dir(Link::INPUT); + connect(pin(1), sig->pin(0)); + connect(pin(2), sub->pin(0)); +} + +NetSubstitute::~NetSubstitute() +{ +} + NetProc::NetProc() : next_(0) { diff --git a/netlist.h b/netlist.h index 02c18e672..7a7ed0b60 100644 --- a/netlist.h +++ b/netlist.h @@ -2192,6 +2192,40 @@ class NetPartSelect : public NetNode { bool signed_flag_; }; +/* + * This device supports simple substitution of a part within a wider + * vector. For example, this: + * + * wire [7:0] foo = NetSubstitute(bar, bat, off); + * + * meaus that bar is a vector the same width as foo, bat is a narrower + * vector. The off is a constant offset into the bar vector. This + * looks something like this: + * + * foo = bar; + * foo[off +: ] = bat; + * + * There is no direct way in Verilog to express this (as a single + * device), it instead turns up in certain synthesis situation, + * i.e. the example above. + */ +class NetSubstitute : public NetNode { + + public: + NetSubstitute(NetNet*sig, NetNet*sub, unsigned wid, unsigned off); + ~NetSubstitute(); + + inline unsigned width() const { return wid_; } + inline unsigned base() const { return off_; } + + virtual void dump_node(ostream&, unsigned ind) const; + virtual bool emit_node(struct target_t*tgt) const; + + private: + unsigned wid_; + unsigned off_; +}; + /* * The NetBUFZ is a magic device that represents the continuous * assign, with the output being the target register and the input diff --git a/synth2.cc b/synth2.cc index c577ca5c9..411aaad56 100644 --- a/synth2.cc +++ b/synth2.cc @@ -68,7 +68,7 @@ bool NetProc::synth_sync(Design*des, NetScope*scope, */ bool NetAssignBase::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&) + NetBus&accumulated_nex_out) { NetNet*rsig = rval_->synthesize(des, scope, rval_); assert(rsig); @@ -103,13 +103,14 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, // Here we note if the l-value is actually a bit/part // select. If so, generate a NetPartSelect to perform the select. - if (lval_->lwidth() != lsig->vector_width()) { + if ((lval_->lwidth()!=lsig->vector_width()) && !scope->loop_index_tmp.empty()) { + // If we are within a NetForLoop, there may be an index + // value. That is collected from the scope member + // loop_index_tmp, and the evaluate_function method + // knows how to apply it. + ivl_assert(*this, !scope->loop_index_tmp.empty()); ivl_assert(*this, lval_->lwidth() < lsig->vector_width()); - // 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. long base_off = 0; // Evaluate the index expression to a constant. @@ -117,16 +118,15 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, ivl_assert(*this, base_expr_raw); NetExpr*base_expr = base_expr_raw->evaluate_function(*this, scope->loop_index_tmp); if (! eval_as_long(base_off, base_expr)) { - assert(0); + ivl_assert(*this, 0); } ivl_assert(*this, base_off >= 0); ivl_variable_type_t tmp_data_type = rsig->data_type(); - listnot_an_array; netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig->vector_width()-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::WIRE, not_an_array, tmp_type); + NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); NetPartSelect*ps = new NetPartSelect(tmp, base_off, lval_->lwidth(), NetPartSelect::PV); @@ -135,6 +135,57 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, connect(ps->pin(0), rsig->pin(0)); rsig = tmp; + + } else if (lval_->lwidth() != lsig->vector_width()) { + // In this case, there is no loop_index_tmp, so we are + // not within a NetForLoop. Generate a NetSubstitute + // object to handle the bit/part-select in the l-value. + ivl_assert(*this, scope->loop_index_tmp.empty()); + ivl_assert(*this, lval_->lwidth() < lsig->vector_width()); + + long base_off = 0; + + const NetExpr*base_expr_raw = lval_->get_base(); + ivl_assert(*this, base_expr_raw); + NetExpr*base_expr = base_expr_raw->evaluate_function(*this, scope->loop_index_tmp); + if (! eval_as_long(base_off, base_expr)) { + ivl_assert(*this, 0); + } + ivl_assert(*this, base_off >= 0); + + ivl_variable_type_t tmp_data_type = rsig->data_type(); + netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig->vector_width()-1,0); + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, NetNet::not_an_array, tmp_type); + //tmp->local_flag(true); + + ivl_assert(*this, accumulated_nex_out.pin_count()==1); + NetNet*use_lsig = lsig; + if (accumulated_nex_out.pin(0).is_linked()) { + if (debug_synth2) { + cerr << get_fileline() << ": NetAssignBase::synth_async: " + << " Found a use_sig:" << endl; + accumulated_nex_out.pin(0).dump_link(cerr, 8); + } + Nexus*tmp_nex = accumulated_nex_out.pin(0).nexus(); + use_lsig = tmp_nex->pick_any_net(); + ivl_assert(*this, use_lsig); + } else { + if (debug_synth2) { + cerr << get_fileline() << ": NetAssignBase::synth_async: " + << " Found no use_sig, resorting to lsig." << endl; + } + } + + NetSubstitute*ps = new NetSubstitute(use_lsig, rsig, + tmp->vector_width(), + base_off); + ps->set_line(*this); + des->add_node(ps); + + connect(ps->pin(0), tmp->pin(0)); + rsig = tmp; } if (nex_out.pin_count() > 1) { @@ -176,6 +227,11 @@ bool NetProc::synth_async_block_substatement_(Design*des, NetScope*scope, << "tmp_map.size()==" << tmp_map.size() << " for statement at " << substmt->get_fileline() << endl; + for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " + << "accumulated_nex_out[" << idx << "] dump link" << endl; + accumulated_nex_out.pin(idx).dump_link(cerr, 8); + } } /* Create also a temporary NetBus to collect the @@ -186,7 +242,6 @@ bool NetProc::synth_async_block_substatement_(Design*des, NetScope*scope, // to the version that we can pass to the next // statement. We will move the result back later. NetBus accumulated_tmp_out (scope, tmp_map.size()); - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { unsigned ptr = tmp_map.find_nexus(nex_map[idx]); if (ptr >= tmp_map.size()) @@ -196,8 +251,29 @@ bool NetProc::synth_async_block_substatement_(Design*des, NetScope*scope, accumulated_nex_out.pin(idx).unlink(); } + if (debug_synth2) { + for (unsigned idx = 0 ; idx < nex_map.size() ; idx += 1) { + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: nex_map[" << idx << "] dump link, base=" << nex_map[idx].base << ", wid=" << nex_map[idx].wid << endl; + nex_map[idx].lnk.dump_link(cerr, 8); + } + for (unsigned idx = 0 ; idx < tmp_map.size() ; idx += 1) { + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: tmp_map[" << idx << "] dump link, base=" << tmp_map[idx].base << ", wid=" << tmp_map[idx].wid << endl; + tmp_map[idx].lnk.dump_link(cerr, 8); + } + for (unsigned idx = 0 ; idx < accumulated_tmp_out.pin_count() ; idx += 1) { + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: accumulated_tmp_out[" << idx << "] dump link" << endl; + accumulated_tmp_out.pin(idx).dump_link(cerr, 8); + } + } + bool ok_flag = substmt->synth_async(des, scope, tmp_map, tmp_out, accumulated_tmp_out); + if (debug_synth2) { + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " + "substmt->synch_async(...) --> " << (ok_flag? "true" : "false") + << " for statement at " << substmt->get_fileline() << "." << endl; + } + if (ok_flag == false) return false; @@ -208,6 +284,11 @@ bool NetProc::synth_async_block_substatement_(Design*des, NetScope*scope, for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { unsigned ptr = nex_map.find_nexus(tmp_map[idx]); ivl_assert(*this, ptr < accumulated_nex_out.pin_count()); + if (debug_synth2) { + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " + << "tmp_out.pin(" << idx << "):" << endl; + tmp_out.pin(idx).dump_link(cerr, 8); + } connect(accumulated_nex_out.pin(ptr), tmp_out.pin(idx)); } @@ -615,12 +696,11 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, cerr << get_fileline() << ": error: Asynchronous if statement" << " cannot synthesize missing \"else\"" << " without generating latches." << endl; - return false; + //return false; } } ivl_assert(*this, if_ != 0); - // Synthesize the condition. This will act as a select signal // for a binary mux. NetNet*ssig = expr_->synthesize(des, scope, expr_); @@ -630,14 +710,26 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, cerr << get_fileline() << ": NetCondit::synth_async: " << "Synthesize if clause at " << if_->get_fileline() << endl; + for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count(); idx += 1) { + cerr << get_fileline() << ": NetCondit::synth_async: " + << "accumulated_nex_out.pin(" << idx << "):" << endl; + accumulated_nex_out.pin(idx).dump_link(cerr, 8); + } } NetBus statement_input (scope, nex_out.pin_count()); + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { + connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); + if (debug_synth2) { + cerr << get_fileline() << ": NetCondit::synth_async: " + << "statement_input.pin(" << idx << "):" << endl; + statement_input.pin(idx).dump_link(cerr, 8); + } + } bool flag; NetBus asig(scope, nex_out.pin_count()); - NetBus atmp(scope, nex_out.pin_count()); - flag = if_->synth_async(des, scope, nex_map, asig, atmp); + flag = if_->synth_async(des, scope, nex_map, asig, accumulated_nex_out); if (!flag) { return false; } @@ -653,29 +745,56 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, } else { - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); - } if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Synthesize else clause at " << else_->get_fileline() << endl; + for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count(); idx += 1) { + cerr << get_fileline() << ": NetCondit::synth_async: " + << "accumulated_nex_out.pin(" << idx << "):" << endl; + accumulated_nex_out.pin(idx).dump_link(cerr, 8); + } + for (unsigned idx = 0 ; idx < statement_input.pin_count() ; idx += 1) { + cerr << get_fileline() << ": NetCondit::synth_async: " + << "statement_input.pin(" << idx << "):" << endl; + statement_input.pin(idx).dump_link(cerr, 8); + } } - flag = synth_async_block_substatement_(des, scope, nex_map, accumulated_nex_out, else_); + NetBus accumulated_btmp_out (scope, statement_input.pin_count()); + for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { + if (statement_input.pin(idx).is_linked()) + connect(accumulated_btmp_out.pin(idx), statement_input.pin(idx)); + else + connect(accumulated_btmp_out.pin(idx), accumulated_nex_out.pin(idx)); + } + if (debug_synth2) { + for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { + cerr << get_fileline() << ": NetCondit::synth_async: " + << "accumulated_btmp_out.pin(" << idx << "):" << endl; + accumulated_btmp_out.pin(idx).dump_link(cerr, 8); + } + } + + flag = synth_async_block_substatement_(des, scope, nex_map, accumulated_btmp_out, else_); if (!flag) { return false; } - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(bsig.pin(idx), accumulated_nex_out.pin(idx)); - accumulated_nex_out.pin(idx).unlink(); - } - if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "synthesize else clause at " << else_->get_fileline() << " is done." << endl; + for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { + cerr << get_fileline() << ": NetCondit::synth_async: " + << "accumulated_btmp_out.pin(" << idx << "):" << endl; + accumulated_btmp_out.pin(idx).dump_link(cerr, 8); + } } + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { + connect(bsig.pin(idx), accumulated_btmp_out.pin(idx)); + accumulated_btmp_out.pin(idx).unlink(); + } + } /* The nex_out output, asig input, and bsig input all have the @@ -724,8 +843,21 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, else mux_width = 0; - const unsigned mux_lwidth = mux_width; - ivl_assert(*this, mux_width != 0); + if (debug_synth2) { + if (asig_is_present) + cerr << get_fileline() << ": NetCondit::synth_async: " + << "asig_is_present," + << " asig width=" << asig.pin(idx).nexus()->vector_width() + << endl; + if (bsig_is_present) + cerr << get_fileline() << ": NetCondit::synth_async: " + << "bsig_is_present," + << " bsig width=" << bsig.pin(idx).nexus()->vector_width() + << endl; + cerr << get_fileline() << ": NetCondit::synth_async: " + << "Calculated mux_width=" << mux_width + << endl; + } NetPartSelect*apv = detect_partselect_lval(asig.pin(idx)); if (debug_synth2 && apv) { @@ -741,6 +873,9 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, << ", width=" << bpv->width() << endl; } + unsigned mux_lwidth = mux_width; + ivl_assert(*this, mux_width != 0); + 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 @@ -754,7 +889,6 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, connect(bsig.pin(idx), bpv->pin(0)); delete apv; delete bpv; - } else { // The part selects are of no use. Forget them. apv = 0; @@ -789,7 +923,6 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, mux_width, 2, 1); mux->set_line(*this); - listnot_an_array; netvector_t*tmp_type = 0; if (mux_width==1) tmp_type = new netvector_t(mux_data_type); @@ -798,7 +931,7 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, // Bind some temporary signals to carry pin type. NetNet*otmp = new NetNet(scope, scope->local_symbol(), - NetNet::WIRE, not_an_array, tmp_type); + NetNet::WIRE, NetNet::not_an_array, tmp_type); otmp->local_flag(true); connect(mux->pin_Result(),otmp->pin(0)); @@ -809,14 +942,14 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, if (! asig_is_present) { tmp_type = new netvector_t(mux_data_type, mux_width-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::WIRE, not_an_array, tmp_type); + NetNet::WIRE, NetNet::not_an_array, tmp_type); connect(mux->pin_Data(1), tmp->pin(0)); } if (! bsig_is_present) { tmp_type = new netvector_t(mux_data_type, mux_width-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::WIRE, not_an_array, tmp_type); + NetNet::WIRE, NetNet::not_an_array, tmp_type); connect(mux->pin_Data(0), tmp->pin(0)); } @@ -826,7 +959,7 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, 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); + NetNet::WIRE, NetNet::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)); @@ -846,7 +979,7 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, if (mux_width < mux_lwidth && if_ && else_) { if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " - << "This MetMux only impacts a few bits of output," + << "This NetMux only impacts a few bits of output," << " so combine nex_out with statement input." << endl; cerr << get_fileline() << ": NetCondit::synth_async: " @@ -999,10 +1132,9 @@ bool NetProcTop::synth_async(Design*des) NexusSet::elem_t&item = nex_set[idx]; if (item.base != 0 || item.wid!=item.lnk.nexus()->vector_width()) { ivl_variable_type_t tmp_data_type = IVL_VT_LOGIC; - listnot_an_array; netvector_t*tmp_type = new netvector_t(tmp_data_type, item.lnk.nexus()->vector_width()-1,0); NetNet*tmp_sig = new NetNet(scope(), scope()->local_symbol(), - NetNet::WIRE, not_an_array, tmp_type); + NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp_sig->local_flag(true); NetPartSelect*tmp = new NetPartSelect(tmp_sig, item.base, diff --git a/t-dll-api.cc b/t-dll-api.cc index 006d70ee7..db622bc3b 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1075,6 +1075,8 @@ extern "C" unsigned ivl_lpm_base(ivl_lpm_t net) case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: return net->u_.part.base; + case IVL_LPM_SUBSTITUTE: + return net->u_.substitute.base; default: assert(0); return 0; @@ -1229,6 +1231,13 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) assert(idx < (net->u_.sfunc.ports-1)); return net->u_.sfunc.pins[idx+1]; + case IVL_LPM_SUBSTITUTE: + assert(idx <= 1); + if (idx == 0) + return net->u_.substitute.a; + else + return net->u_.substitute.s; + case IVL_LPM_UFUNC: // Skip the return port. assert(idx < (net->u_.ufunc.ports-1)); @@ -1357,6 +1366,9 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net) case IVL_LPM_REPEAT: return net->u_.repeat.q; + case IVL_LPM_SUBSTITUTE: + return net->u_.substitute.q; + case IVL_LPM_ARRAY: return net->u_.array.q; @@ -1488,6 +1500,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) case IVL_LPM_PART_PV: return net->u_.part.signed_flag; case IVL_LPM_REPEAT: + case IVL_LPM_SUBSTITUTE: return 0; case IVL_LPM_ARRAY: // Array ports take the signedness of the array. return net->u_.array.sig->net_type->get_signed()? 1 : 0; diff --git a/t-dll.cc b/t-dll.cc index 6eff6ce08..22f1e68ae 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1100,6 +1100,32 @@ bool dll_target::tran(const NetTran*net) return true; } +bool dll_target::substitute(const NetSubstitute*net) +{ + ivl_lpm_t obj = new struct ivl_lpm_s; + obj->type = IVL_LPM_SUBSTITUTE; + obj->name = net->name(); + assert(net->scope()); + obj->scope = find_scope(des_, net->scope()); + assert(obj->scope); + FILE_NAME(obj, net); + + obj->width = net->width(); + obj->u_.substitute.base = net->base(); + + obj->u_.substitute.q = net->pin(0).nexus()->t_cookie(); + obj->u_.substitute.a = net->pin(1).nexus()->t_cookie(); + obj->u_.substitute.s = net->pin(2).nexus()->t_cookie(); + nexus_lpm_add(obj->u_.substitute.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); + nexus_lpm_add(obj->u_.substitute.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); + nexus_lpm_add(obj->u_.substitute.s, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); + + make_lpm_delays_(obj, net); + scope_add_lpm(obj->scope, obj); + + return true; +} + bool dll_target::sign_extend(const NetSignExtend*net) { struct ivl_lpm_s*obj = new struct ivl_lpm_s; diff --git a/t-dll.h b/t-dll.h index d9ae83c74..60c2e1df5 100644 --- a/t-dll.h +++ b/t-dll.h @@ -90,6 +90,7 @@ struct dll_target : public target_t, public expr_scan_t { bool net_literal(const NetLiteral*); void net_probe(const NetEvProbe*); bool sign_extend(const NetSignExtend*); + bool substitute(const NetSubstitute*); bool process(const NetProcTop*); bool process(const NetAnalogTop*); @@ -437,6 +438,11 @@ struct ivl_lpm_s { ivl_event_t trigger; } sfunc; + struct ivl_lpm_substitute { + unsigned base; + ivl_nexus_t q, a, s; + } substitute; + struct ivl_lpm_ufunc_s { ivl_scope_t def; unsigned ports; diff --git a/target.cc b/target.cc index bb5bfb8c1..4b9f4171b 100644 --- a/target.cc +++ b/target.cc @@ -270,6 +270,13 @@ bool target_t::sign_extend(const NetSignExtend*) return false; } +bool target_t::substitute(const NetSubstitute*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetSubstitute node." << endl; + return false; +} + bool target_t::process(const NetProcTop*) { cerr << "target (" << typeid(*this).name() << "): " diff --git a/target.h b/target.h index cf87bea9d..5860fc464 100644 --- a/target.h +++ b/target.h @@ -116,6 +116,7 @@ struct target_t { virtual bool net_literal(const NetLiteral*); virtual void net_probe(const NetEvProbe*); virtual bool sign_extend(const NetSignExtend*); + virtual bool substitute(const NetSubstitute*); /* Output a process (called for each process). It is up to the target to recurse if desired. */ diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index 4468c131f..1a61a1906 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -860,6 +860,37 @@ static void show_lpm_sub(ivl_lpm_t net) show_lpm_arithmetic_pins(net); } +static void show_lpm_substitute(ivl_lpm_t net) +{ + unsigned width = ivl_lpm_width(net); + ivl_nexus_t nex_q = ivl_lpm_q(net); + ivl_nexus_t nex_a = ivl_lpm_data(net,0); + ivl_nexus_t nex_s = ivl_lpm_data(net,1); + + unsigned sbase = ivl_lpm_base(net); + unsigned swidth = width_of_nexus(nex_s); + + fprintf(out, " LPM_SUBSTITUTE %s: \n", + ivl_lpm_basename(net), width, sbase, swidth); + fprintf(out, " Q: %p\n", nex_q); + if (width != width_of_nexus(nex_q)) { + fprintf(out, " ERROR: Width of Q is %u, expecting %u\n", + width_of_nexus(nex_q), width); + stub_errors += 1; + } + fprintf(out, " A: %p\n", nex_a); + if (width != width_of_nexus(nex_a)) { + fprintf(out, " ERROR: Width of A is %u, expecting %u\n", + width_of_nexus(nex_a), width); + stub_errors += 1; + } + fprintf(out, " S: %p\n", nex_s); + if (sbase + swidth > width) { + fprintf(out, " ERROR: S part is out of bounds\n"); + stub_errors += 1; + } +} + static void show_lpm_sfunc(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); @@ -1024,6 +1055,10 @@ static void show_lpm(ivl_lpm_t net) show_lpm_sub(net); break; + case IVL_LPM_SUBSTITUTE: + show_lpm_substitute(net); + break; + case IVL_LPM_MOD: show_lpm_mod(net); break; diff --git a/tgt-vvp/Makefile.in b/tgt-vvp/Makefile.in index 781374442..ddbd306f9 100644 --- a/tgt-vvp/Makefile.in +++ b/tgt-vvp/Makefile.in @@ -47,7 +47,7 @@ CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ LDFLAGS = @LDFLAGS@ -O = vvp.o draw_class.o draw_enum.o draw_mux.o draw_net_input.o \ +O = vvp.o draw_class.o draw_enum.o draw_mux.o draw_substitute.o draw_net_input.o \ draw_switch.o draw_ufunc.o draw_vpi.o \ eval_bool.o eval_expr.o eval_object.o eval_real.o eval_string.o \ modpath.o stmt_assign.o vector.o \ diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index 4a1c345be..906be39a0 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -476,6 +476,7 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */ case IVL_LPM_REPEAT: + case IVL_LPM_SUBSTITUTE: if (ivl_lpm_q(lpm) == nex) { char tmp[128]; snprintf(tmp, sizeof tmp, "L_%p", lpm); diff --git a/tgt-vvp/draw_substitute.c b/tgt-vvp/draw_substitute.c new file mode 100644 index 000000000..22d5c4223 --- /dev/null +++ b/tgt-vvp/draw_substitute.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +# include "vvp_priv.h" +# include +# include +# include + +void draw_lpm_substitute(ivl_lpm_t net) +{ + unsigned swidth = width_of_nexus(ivl_lpm_data(net,1)); + fprintf(vvp_out, "L_%p .substitute %u, %u %u", + net, ivl_lpm_width(net), ivl_lpm_base(net), swidth); + fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,0))); + fprintf(vvp_out, ", %s;\n", draw_net_input(ivl_lpm_data(net,1))); +} diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index ef7afe9ba..5533e9556 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -103,6 +103,7 @@ extern int draw_func_definition(ivl_scope_t scope); extern int draw_scope(ivl_scope_t scope, ivl_scope_t parent); extern void draw_lpm_mux(ivl_lpm_t net); +extern void draw_lpm_substitute(ivl_lpm_t net); extern struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid); extern void draw_ufunc_real(ivl_expr_t expr); diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index edbab12a4..6cdae7eb9 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -2134,6 +2134,10 @@ static void draw_lpm_in_scope(ivl_lpm_t net) draw_lpm_sfunc(net); return; + case IVL_LPM_SUBSTITUTE: + draw_lpm_substitute(net); + return; + case IVL_LPM_UFUNC: draw_lpm_ufunc(net); return; diff --git a/vvp/Makefile.in b/vvp/Makefile.in index 61d9fc5c0..16a0ecbc2 100644 --- a/vvp/Makefile.in +++ b/vvp/Makefile.in @@ -74,7 +74,9 @@ V = vpi_modules.o vpi_callback.o vpi_cobject.o vpi_const.o vpi_darray.o \ O = main.o parse.o parse_misc.o lexor.o arith.o array.o bufif.o compile.o \ concat.o dff.o class_type.o enum_type.o extend.o file_line.o npmos.o part.o \ permaheap.o reduce.o resolv.o \ - sfunc.o stop.o symbols.o ufunc.o codes.o vthread.o schedule.o \ + sfunc.o stop.o \ + substitute.o \ + symbols.o ufunc.o codes.o vthread.o schedule.o \ statistics.o tables.o udp.o vvp_island.o vvp_net.o vvp_net_sig.o \ vvp_object.o vvp_cobject.o vvp_darray.o event.o logic.o delay.o \ words.o island_tran.o $V diff --git a/vvp/README.txt b/vvp/README.txt index c1a9c0409..7687fdbb8 100644 --- a/vvp/README.txt +++ b/vvp/README.txt @@ -533,6 +533,22 @@ the *output* vector. The is the number of time the input vector value is repeated to make the output width. The input width is implicit from these numbers. The is then the input source. +SUBSTITUTION STATEMENTS: + +The substition statement doesn't have a direct analog in Verilog, it +only turns up in synthesis. It is a sorthand for forms like this: + + foo = ; + foo[n] = ; + +The format of the substitute statement is: + +