Handle some tricky conditions assignments to parts.

When for example assigning to foo[<x>] within a contitional, and
doing synthesis, we need to create a NetSubstitute device to manage
the l-value bit selects.
This commit is contained in:
Stephen Williams 2014-07-13 18:09:13 -07:00
parent c1e533d484
commit d5fb0f4344
24 changed files with 499 additions and 51 deletions

View File

@ -778,6 +778,21 @@ void NetPartSelect::dump_node(ostream&o, unsigned ind) const
dump_obj_attr(o, ind+4); 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_ <<endl;
dump_node_pins(fd, ind+4);
dump_obj_attr(fd, ind+4);
}
void NetReplicate::dump_node(ostream&o, unsigned ind) const void NetReplicate::dump_node(ostream&o, unsigned ind) const
{ {
o << setw(ind) << "" << "NetReplicate: " o << setw(ind) << "" << "NetReplicate: "

View File

@ -167,6 +167,11 @@ bool NetSignExtend::emit_node(struct target_t*tgt) const
return tgt->sign_extend(this); return tgt->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 bool NetUReduce::emit_node(struct target_t*tgt) const
{ {
return tgt->ureduce(this); return tgt->ureduce(this);

View File

@ -328,6 +328,7 @@ typedef enum ivl_lpm_type_e {
IVL_LPM_SHIFTR = 7, IVL_LPM_SHIFTR = 7,
IVL_LPM_SIGN_EXT=27, IVL_LPM_SIGN_EXT=27,
IVL_LPM_SUB = 8, IVL_LPM_SUB = 8,
IVL_LPM_SUBSTITUTE=39,
/* IVL_LPM_RAM = 9, / obsolete */ /* IVL_LPM_RAM = 9, / obsolete */
IVL_LPM_UFUNC = 14 IVL_LPM_UFUNC = 14
} ivl_lpm_type_t; } 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); extern ivl_expr_t ivl_lpm_sset_value(ivl_lpm_t net);
/* IVL_LPM_ARRAY */ /* IVL_LPM_ARRAY */
extern ivl_signal_t ivl_lpm_array(ivl_lpm_t net); 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); extern unsigned ivl_lpm_base(ivl_lpm_t net);
/* IVL_LPM_FF */ /* IVL_LPM_FF */
extern ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net); 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); 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_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_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); 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_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 */ 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); 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_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_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_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_drive0(ivl_lpm_t net);
extern ivl_drive_t ivl_lpm_drive1(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive1(ivl_lpm_t net);

View File

@ -60,19 +60,18 @@ void NetAssign_::nex_output(NexusSet&out)
} }
Nexus*nex = sig_->pin(use_word).nexus(); Nexus*nex = sig_->pin(use_word).nexus();
if (base_) { if (base_) {
long tmp = 0;
bool flag = eval_as_long(tmp, base_);
if (!flag) {
// Unable to evaluate the bit/part select of // Unable to evaluate the bit/part select of
// the l-value, so this is a mux. Pretty // the l-value, so this is a mux. Pretty
// sure I don't know how to handle this yet // sure I don't know how to handle this yet
// in synthesis, so punt for now. // 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_base = 0;
use_wid = nex->vector_width(); use_wid = nex->vector_width();
} else {
use_base = tmp;
}
} }
out.add(nex, use_base, use_wid); out.add(nex, use_base, use_wid);
} }

View File

@ -989,6 +989,20 @@ unsigned NetPartSelect::base() const
return off_; 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() NetProc::NetProc()
: next_(0) : next_(0)
{ {

View File

@ -2192,6 +2192,40 @@ class NetPartSelect : public NetNode {
bool signed_flag_; 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 +: <width_of_bat>] = 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 * The NetBUFZ is a magic device that represents the continuous
* assign, with the output being the target register and the input * assign, with the output being the target register and the input

200
synth2.cc
View File

@ -68,7 +68,7 @@ bool NetProc::synth_sync(Design*des, NetScope*scope,
*/ */
bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool NetAssignBase::synth_async(Design*des, NetScope*scope,
NexusSet&nex_map, NetBus&nex_out, NexusSet&nex_map, NetBus&nex_out,
NetBus&) NetBus&accumulated_nex_out)
{ {
NetNet*rsig = rval_->synthesize(des, scope, rval_); NetNet*rsig = rval_->synthesize(des, scope, rval_);
assert(rsig); 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 // Here we note if the l-value is actually a bit/part
// select. If so, generate a NetPartSelect to perform the select. // 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()); 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; long base_off = 0;
// Evaluate the index expression to a constant. // 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); ivl_assert(*this, base_expr_raw);
NetExpr*base_expr = base_expr_raw->evaluate_function(*this, scope->loop_index_tmp); NetExpr*base_expr = base_expr_raw->evaluate_function(*this, scope->loop_index_tmp);
if (! eval_as_long(base_off, base_expr)) { if (! eval_as_long(base_off, base_expr)) {
assert(0); ivl_assert(*this, 0);
} }
ivl_assert(*this, base_off >= 0); ivl_assert(*this, base_off >= 0);
ivl_variable_type_t tmp_data_type = rsig->data_type(); ivl_variable_type_t tmp_data_type = rsig->data_type();
list<netrange_t>not_an_array;
netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig->vector_width()-1,0); netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig->vector_width()-1,0);
NetNet*tmp = new NetNet(scope, scope->local_symbol(), 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); tmp->local_flag(true);
NetPartSelect*ps = new NetPartSelect(tmp, base_off, lval_->lwidth(), NetPartSelect::PV); 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)); connect(ps->pin(0), rsig->pin(0));
rsig = tmp; 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) { 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() << "tmp_map.size()==" << tmp_map.size()
<< " for statement at " << substmt->get_fileline() << " for statement at " << substmt->get_fileline()
<< endl; << 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 /* 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 // to the version that we can pass to the next
// statement. We will move the result back later. // statement. We will move the result back later.
NetBus accumulated_tmp_out (scope, tmp_map.size()); NetBus accumulated_tmp_out (scope, tmp_map.size());
for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) {
unsigned ptr = tmp_map.find_nexus(nex_map[idx]); unsigned ptr = tmp_map.find_nexus(nex_map[idx]);
if (ptr >= tmp_map.size()) 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(); 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); 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) if (ok_flag == false)
return 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) { for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) {
unsigned ptr = nex_map.find_nexus(tmp_map[idx]); unsigned ptr = nex_map.find_nexus(tmp_map[idx]);
ivl_assert(*this, ptr < accumulated_nex_out.pin_count()); 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)); 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" cerr << get_fileline() << ": error: Asynchronous if statement"
<< " cannot synthesize missing \"else\"" << " cannot synthesize missing \"else\""
<< " without generating latches." << endl; << " without generating latches." << endl;
return false; //return false;
} }
} }
ivl_assert(*this, if_ != 0); ivl_assert(*this, if_ != 0);
// Synthesize the condition. This will act as a select signal // Synthesize the condition. This will act as a select signal
// for a binary mux. // for a binary mux.
NetNet*ssig = expr_->synthesize(des, scope, expr_); 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: " cerr << get_fileline() << ": NetCondit::synth_async: "
<< "Synthesize if clause at " << if_->get_fileline() << "Synthesize if clause at " << if_->get_fileline()
<< endl; << 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()); 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; bool flag;
NetBus asig(scope, nex_out.pin_count()); NetBus asig(scope, nex_out.pin_count());
NetBus atmp(scope, nex_out.pin_count()); flag = if_->synth_async(des, scope, nex_map, asig, accumulated_nex_out);
flag = if_->synth_async(des, scope, nex_map, asig, atmp);
if (!flag) { if (!flag) {
return false; return false;
} }
@ -653,29 +745,56 @@ bool NetCondit::synth_async(Design*des, NetScope*scope,
} else { } 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) { if (debug_synth2) {
cerr << get_fileline() << ": NetCondit::synth_async: " cerr << get_fileline() << ": NetCondit::synth_async: "
<< "Synthesize else clause at " << else_->get_fileline() << "Synthesize else clause at " << else_->get_fileline()
<< endl; << 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) { if (!flag) {
return false; 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) { if (debug_synth2) {
cerr << get_fileline() << ": NetCondit::synth_async: " cerr << get_fileline() << ": NetCondit::synth_async: "
<< "synthesize else clause at " << else_->get_fileline() << "synthesize else clause at " << else_->get_fileline()
<< " is done." << endl; << " 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 /* 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 else
mux_width = 0; mux_width = 0;
const unsigned mux_lwidth = mux_width; if (debug_synth2) {
ivl_assert(*this, mux_width != 0); 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)); NetPartSelect*apv = detect_partselect_lval(asig.pin(idx));
if (debug_synth2 && apv) { if (debug_synth2 && apv) {
@ -741,6 +873,9 @@ bool NetCondit::synth_async(Design*des, NetScope*scope,
<< ", width=" << bpv->width() << endl; << ", 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()) { if (apv && bpv && apv->width()==bpv->width() && apv->base()==bpv->base()) {
// The a and b sides are both assigning to the // The a and b sides are both assigning to the
// same bits of the output, so we can use that to // 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)); connect(bsig.pin(idx), bpv->pin(0));
delete apv; delete apv;
delete bpv; delete bpv;
} else { } else {
// The part selects are of no use. Forget them. // The part selects are of no use. Forget them.
apv = 0; apv = 0;
@ -789,7 +923,6 @@ bool NetCondit::synth_async(Design*des, NetScope*scope,
mux_width, 2, 1); mux_width, 2, 1);
mux->set_line(*this); mux->set_line(*this);
list<netrange_t>not_an_array;
netvector_t*tmp_type = 0; netvector_t*tmp_type = 0;
if (mux_width==1) if (mux_width==1)
tmp_type = new netvector_t(mux_data_type); 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. // Bind some temporary signals to carry pin type.
NetNet*otmp = new NetNet(scope, scope->local_symbol(), 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); otmp->local_flag(true);
connect(mux->pin_Result(),otmp->pin(0)); connect(mux->pin_Result(),otmp->pin(0));
@ -809,14 +942,14 @@ bool NetCondit::synth_async(Design*des, NetScope*scope,
if (! asig_is_present) { if (! asig_is_present) {
tmp_type = new netvector_t(mux_data_type, mux_width-1,0); tmp_type = new netvector_t(mux_data_type, mux_width-1,0);
NetNet*tmp = new NetNet(scope, scope->local_symbol(), 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)); connect(mux->pin_Data(1), tmp->pin(0));
} }
if (! bsig_is_present) { if (! bsig_is_present) {
tmp_type = new netvector_t(mux_data_type, mux_width-1,0); tmp_type = new netvector_t(mux_data_type, mux_width-1,0);
NetNet*tmp = new NetNet(scope, scope->local_symbol(), 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)); 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) { if (mux_width < mux_lwidth) {
tmp_type = new netvector_t(mux_data_type, mux_lwidth-1,0); tmp_type = new netvector_t(mux_data_type, mux_lwidth-1,0);
NetNet*tmp = new NetNet(scope, scope->local_symbol(), 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); NetPartSelect*ps = new NetPartSelect(tmp, mux_off, mux_width, NetPartSelect::PV);
des->add_node(ps); des->add_node(ps);
connect(ps->pin(0), otmp->pin(0)); 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 (mux_width < mux_lwidth && if_ && else_) {
if (debug_synth2) { if (debug_synth2) {
cerr << get_fileline() << ": NetCondit::synth_async: " 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." << " so combine nex_out with statement input."
<< endl; << endl;
cerr << get_fileline() << ": NetCondit::synth_async: " cerr << get_fileline() << ": NetCondit::synth_async: "
@ -999,10 +1132,9 @@ bool NetProcTop::synth_async(Design*des)
NexusSet::elem_t&item = nex_set[idx]; NexusSet::elem_t&item = nex_set[idx];
if (item.base != 0 || item.wid!=item.lnk.nexus()->vector_width()) { if (item.base != 0 || item.wid!=item.lnk.nexus()->vector_width()) {
ivl_variable_type_t tmp_data_type = IVL_VT_LOGIC; ivl_variable_type_t tmp_data_type = IVL_VT_LOGIC;
list<netrange_t>not_an_array;
netvector_t*tmp_type = new netvector_t(tmp_data_type, item.lnk.nexus()->vector_width()-1,0); 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*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); tmp_sig->local_flag(true);
NetPartSelect*tmp = new NetPartSelect(tmp_sig, item.base, NetPartSelect*tmp = new NetPartSelect(tmp_sig, item.base,

View File

@ -1075,6 +1075,8 @@ extern "C" unsigned ivl_lpm_base(ivl_lpm_t net)
case IVL_LPM_PART_VP: case IVL_LPM_PART_VP:
case IVL_LPM_PART_PV: case IVL_LPM_PART_PV:
return net->u_.part.base; return net->u_.part.base;
case IVL_LPM_SUBSTITUTE:
return net->u_.substitute.base;
default: default:
assert(0); assert(0);
return 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)); assert(idx < (net->u_.sfunc.ports-1));
return net->u_.sfunc.pins[idx+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: case IVL_LPM_UFUNC:
// Skip the return port. // Skip the return port.
assert(idx < (net->u_.ufunc.ports-1)); 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: case IVL_LPM_REPEAT:
return net->u_.repeat.q; return net->u_.repeat.q;
case IVL_LPM_SUBSTITUTE:
return net->u_.substitute.q;
case IVL_LPM_ARRAY: case IVL_LPM_ARRAY:
return net->u_.array.q; return net->u_.array.q;
@ -1488,6 +1500,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net)
case IVL_LPM_PART_PV: case IVL_LPM_PART_PV:
return net->u_.part.signed_flag; return net->u_.part.signed_flag;
case IVL_LPM_REPEAT: case IVL_LPM_REPEAT:
case IVL_LPM_SUBSTITUTE:
return 0; return 0;
case IVL_LPM_ARRAY: // Array ports take the signedness of the array. case IVL_LPM_ARRAY: // Array ports take the signedness of the array.
return net->u_.array.sig->net_type->get_signed()? 1 : 0; return net->u_.array.sig->net_type->get_signed()? 1 : 0;

View File

@ -1100,6 +1100,32 @@ bool dll_target::tran(const NetTran*net)
return true; 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) bool dll_target::sign_extend(const NetSignExtend*net)
{ {
struct ivl_lpm_s*obj = new struct ivl_lpm_s; struct ivl_lpm_s*obj = new struct ivl_lpm_s;

View File

@ -90,6 +90,7 @@ struct dll_target : public target_t, public expr_scan_t {
bool net_literal(const NetLiteral*); bool net_literal(const NetLiteral*);
void net_probe(const NetEvProbe*); void net_probe(const NetEvProbe*);
bool sign_extend(const NetSignExtend*); bool sign_extend(const NetSignExtend*);
bool substitute(const NetSubstitute*);
bool process(const NetProcTop*); bool process(const NetProcTop*);
bool process(const NetAnalogTop*); bool process(const NetAnalogTop*);
@ -437,6 +438,11 @@ struct ivl_lpm_s {
ivl_event_t trigger; ivl_event_t trigger;
} sfunc; } sfunc;
struct ivl_lpm_substitute {
unsigned base;
ivl_nexus_t q, a, s;
} substitute;
struct ivl_lpm_ufunc_s { struct ivl_lpm_ufunc_s {
ivl_scope_t def; ivl_scope_t def;
unsigned ports; unsigned ports;

View File

@ -270,6 +270,13 @@ bool target_t::sign_extend(const NetSignExtend*)
return false; 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*) bool target_t::process(const NetProcTop*)
{ {
cerr << "target (" << typeid(*this).name() << "): " cerr << "target (" << typeid(*this).name() << "): "

View File

@ -116,6 +116,7 @@ struct target_t {
virtual bool net_literal(const NetLiteral*); virtual bool net_literal(const NetLiteral*);
virtual void net_probe(const NetEvProbe*); virtual void net_probe(const NetEvProbe*);
virtual bool sign_extend(const NetSignExtend*); virtual bool sign_extend(const NetSignExtend*);
virtual bool substitute(const NetSubstitute*);
/* Output a process (called for each process). It is up to the /* Output a process (called for each process). It is up to the
target to recurse if desired. */ target to recurse if desired. */

View File

@ -860,6 +860,37 @@ static void show_lpm_sub(ivl_lpm_t net)
show_lpm_arithmetic_pins(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: <width=%u, sbase=%u, swidth=%u>\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) static void show_lpm_sfunc(ivl_lpm_t net)
{ {
unsigned width = ivl_lpm_width(net); unsigned width = ivl_lpm_width(net);
@ -1024,6 +1055,10 @@ static void show_lpm(ivl_lpm_t net)
show_lpm_sub(net); show_lpm_sub(net);
break; break;
case IVL_LPM_SUBSTITUTE:
show_lpm_substitute(net);
break;
case IVL_LPM_MOD: case IVL_LPM_MOD:
show_lpm_mod(net); show_lpm_mod(net);
break; break;

View File

@ -47,7 +47,7 @@ CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@
CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ CFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@
LDFLAGS = @LDFLAGS@ 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 \ draw_switch.o draw_ufunc.o draw_vpi.o \
eval_bool.o eval_expr.o eval_object.o eval_real.o eval_string.o \ eval_bool.o eval_expr.o eval_object.o eval_real.o eval_string.o \
modpath.o stmt_assign.o vector.o \ modpath.o stmt_assign.o vector.o \

View File

@ -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_VP:
case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */ case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */
case IVL_LPM_REPEAT: case IVL_LPM_REPEAT:
case IVL_LPM_SUBSTITUTE:
if (ivl_lpm_q(lpm) == nex) { if (ivl_lpm_q(lpm) == nex) {
char tmp[128]; char tmp[128];
snprintf(tmp, sizeof tmp, "L_%p", lpm); snprintf(tmp, sizeof tmp, "L_%p", lpm);

32
tgt-vvp/draw_substitute.c Normal file
View File

@ -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 <string.h>
# include <stdlib.h>
# include <assert.h>
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)));
}

View File

@ -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 int draw_scope(ivl_scope_t scope, ivl_scope_t parent);
extern void draw_lpm_mux(ivl_lpm_t net); 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 struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid);
extern void draw_ufunc_real(ivl_expr_t expr); extern void draw_ufunc_real(ivl_expr_t expr);

View File

@ -2134,6 +2134,10 @@ static void draw_lpm_in_scope(ivl_lpm_t net)
draw_lpm_sfunc(net); draw_lpm_sfunc(net);
return; return;
case IVL_LPM_SUBSTITUTE:
draw_lpm_substitute(net);
return;
case IVL_LPM_UFUNC: case IVL_LPM_UFUNC:
draw_lpm_ufunc(net); draw_lpm_ufunc(net);
return; return;

View File

@ -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 \ 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 \ 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 \ 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 \ 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 \ vvp_object.o vvp_cobject.o vvp_darray.o event.o logic.o delay.o \
words.o island_tran.o $V words.o island_tran.o $V

View File

@ -533,6 +533,22 @@ the *output* vector. The <rept count> is the number of time the input
vector value is repeated to make the output width. The input width is vector value is repeated to make the output width. The input width is
implicit from these numbers. The <symbol> is then the input source. implicit from these numbers. The <symbol> 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 = <a>;
foo[n] = <s>;
The format of the substitute statement is:
<label> .substitute <wid>, <soff> <swid>, <symbol>, <symbol> ;
The first <symbol> must have the width <wid>, and is passed through,
except for the bits within [<soff> +: <swid>]. The second <symbol>
collects a vector that goes into that part.
REDUCTION LOGIC REDUCTION LOGIC
The reduction logic statements take in a single vector, and propagate The reduction logic statements take in a single vector, and propagate

View File

@ -124,6 +124,10 @@ extern void compile_concat8(char*label, unsigned w0, unsigned w1,
unsigned w2, unsigned w3, unsigned w2, unsigned w3,
unsigned argc, struct symb_s*argv); unsigned argc, struct symb_s*argv);
extern void compile_substitute(char*label, unsigned width,
unsigned soff, unsigned swidth,
unsigned argc, struct symb_s*argv);
/* /*
* Arrange for the system task/function call to have its compiletf * Arrange for the system task/function call to have its compiletf
* function called. * function called.

View File

@ -197,6 +197,7 @@ static char* strdupnew(char const *str)
".shift/l" { return K_SHIFTL; } ".shift/l" { return K_SHIFTL; }
".shift/r" { return K_SHIFTR; } ".shift/r" { return K_SHIFTR; }
".shift/rs" { return K_SHIFTRS; } ".shift/rs" { return K_SHIFTRS; }
".substitute" { return K_SUBSTITUTE; }
".thread" { return K_THREAD; } ".thread" { return K_THREAD; }
".timescale" { return K_TIMESCALE; } ".timescale" { return K_TIMESCALE; }
".tran" { return K_TRAN; } ".tran" { return K_TRAN; }

View File

@ -92,6 +92,7 @@ static struct __vpiModPath*modpath_dst = 0;
%token K_PART_V K_PART_V_S K_PORT K_PORT_INFO K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR %token K_PART_V K_PART_V_S K_PORT K_PORT_INFO 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_REDUCE_NAND K_REDUCE_NOR K_REDUCE_XNOR K_REPEAT
%token K_RESOLV K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS %token K_RESOLV K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS
%token K_SUBSTITUTE
%token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP %token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP
%token K_UFUNC K_UFUNC_E K_UDP K_UDP_C K_UDP_S %token K_UFUNC K_UFUNC_E K_UDP K_UDP_C K_UDP_S
%token K_VAR K_VAR_COBJECT K_VAR_DARRAY %token K_VAR K_VAR_COBJECT K_VAR_DARRAY
@ -294,6 +295,10 @@ statement
symbols ';' symbols ';'
{ compile_concat8($1, $4, $5, $6, $7, $10.cnt, $10.vect); } { compile_concat8($1, $4, $5, $6, $7, $10.cnt, $10.vect); }
/* Substitutions (similar to concatenations) */
| T_LABEL K_SUBSTITUTE T_NUMBER ',' T_NUMBER T_NUMBER ',' symbols ';'
{ compile_substitute($1, $3, $5, $6, $8.cnt, $8.vect); }
/* The ABS statement is a special arithmetic node that takes 1 /* The ABS statement is a special arithmetic node that takes 1
input. Re-use the symbols rule. */ input. Re-use the symbols rule. */

94
vvp/substitute.cc Normal file
View File

@ -0,0 +1,94 @@
/*
* 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 "compile.h"
# include "vvp_net.h"
# include <cstdlib>
# include <iostream>
# include <cassert>
class vvp_fun_substitute : public vvp_net_fun_t {
public:
vvp_fun_substitute(unsigned wid, unsigned soff, unsigned swid);
~vvp_fun_substitute();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_context_t);
private:
unsigned wid_;
unsigned soff_;
unsigned swid_;
vvp_vector4_t val_;
};
vvp_fun_substitute::vvp_fun_substitute(unsigned wid, unsigned soff, unsigned swid)
: wid_(wid), soff_(soff), swid_(swid), val_(wid)
{
for (unsigned idx = 0 ; idx < val_.size() ; idx += 1)
val_.set_bit(idx, BIT4_Z);
}
vvp_fun_substitute::~vvp_fun_substitute()
{
}
void vvp_fun_substitute::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t)
{
unsigned pdx = port.port();
assert(pdx <= 1);
if (pdx == 0) {
assert(bit.size() == wid_);
for (unsigned idx = 0 ; idx < wid_ ; idx += 1) {
if (idx >= soff_ && idx < (soff_+swid_))
continue;
val_.set_bit(idx, bit.value(idx));
}
} else {
assert(bit.size() == swid_);
for (unsigned idx = 0 ; idx < swid_ ; idx += 1)
val_.set_bit(idx+soff_, bit.value(idx));
}
port.ptr()->send_vec4(val_, 0);
}
void compile_substitute(char*label, unsigned width,
unsigned soff, unsigned swidth,
unsigned argc, struct symb_s*argv)
{
vvp_fun_substitute*fun = new vvp_fun_substitute(width, soff, swidth);
vvp_net_t*net = new vvp_net_t;
net->fun = fun;
define_functor_symbol(label, net);
free(label);
inputs_connect(net, argc, argv);
free(argv);
}