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:
parent
c1e533d484
commit
d5fb0f4344
|
|
@ -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: "
|
||||
|
|
|
|||
5
emit.cc
5
emit.cc
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
14
netlist.cc
14
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)
|
||||
{
|
||||
|
|
|
|||
34
netlist.h
34
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 +: <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
200
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();
|
||||
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,
|
||||
|
|
|
|||
13
t-dll-api.cc
13
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;
|
||||
|
|
|
|||
26
t-dll.cc
26
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;
|
||||
|
|
|
|||
6
t-dll.h
6
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;
|
||||
|
|
|
|||
|
|
@ -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() << "): "
|
||||
|
|
|
|||
1
target.h
1
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. */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
Loading…
Reference in New Issue