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);
}
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
{
o << setw(ind) << "" << "NetReplicate: "

View File

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

View File

@ -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);

View File

@ -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);
}

View File

@ -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)
{

View File

@ -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 +: <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
* 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,
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();
list<netrange_t>not_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);
list<netrange_t>not_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;
list<netrange_t>not_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,

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_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;

View File

@ -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;

View File

@ -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;

View File

@ -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() << "): "

View File

@ -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. */

View File

@ -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: <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)
{
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;

View File

@ -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 \

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_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);

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 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);

View File

@ -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;

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 \
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

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
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
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 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
* function called.

View File

@ -197,6 +197,7 @@ static char* strdupnew(char const *str)
".shift/l" { return K_SHIFTL; }
".shift/r" { return K_SHIFTR; }
".shift/rs" { return K_SHIFTRS; }
".substitute" { return K_SUBSTITUTE; }
".thread" { return K_THREAD; }
".timescale" { return K_TIMESCALE; }
".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_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_SUBSTITUTE
%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_VAR K_VAR_COBJECT K_VAR_DARRAY
@ -294,6 +295,10 @@ statement
symbols ';'
{ 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
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);
}