2002-06-30 04:21:31 +02:00
|
|
|
/*
|
2016-01-30 18:01:13 +01:00
|
|
|
* Copyright (c) 2002-2016 Stephen Williams (steve@icarus.com)
|
2002-06-30 04:21:31 +02:00
|
|
|
*
|
|
|
|
|
* 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
|
2012-08-29 03:41:23 +02:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2002-06-30 04:21:31 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
# include "config.h"
|
|
|
|
|
|
|
|
|
|
# include "functor.h"
|
|
|
|
|
# include "netlist.h"
|
2012-09-15 19:27:43 +02:00
|
|
|
# include "netvector.h"
|
2005-04-25 01:44:01 +02:00
|
|
|
# include "netmisc.h"
|
2002-07-01 02:54:21 +02:00
|
|
|
# include "compiler.h"
|
2013-08-12 04:10:50 +02:00
|
|
|
# include "ivl_assert.h"
|
2002-06-30 04:21:31 +02:00
|
|
|
|
2013-08-23 05:00:23 +02:00
|
|
|
using namespace std;
|
2004-03-15 19:40:12 +01:00
|
|
|
|
2014-02-15 23:16:22 +01:00
|
|
|
bool NetProc::synth_async(Design*, NetScope*, NexusSet&, NetBus&, NetBus&)
|
2002-09-16 02:30:33 +02:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-25 04:39:16 +02:00
|
|
|
bool NetProc::synth_sync(Design*des, NetScope*scope,
|
2015-06-13 16:56:12 +02:00
|
|
|
bool& /* ff_negedge */,
|
2014-07-20 00:17:53 +02:00
|
|
|
NetNet* /* ff_clk */, NetBus& /* ff_ce */,
|
2014-06-16 04:10:09 +02:00
|
|
|
NetBus& /* ff_aclr*/, NetBus& /* ff_aset*/,
|
2015-06-13 16:56:12 +02:00
|
|
|
vector<verinum>& /*ff_aset_value*/,
|
2014-02-15 23:16:22 +01:00
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
2013-08-23 05:00:23 +02:00
|
|
|
const vector<NetEvProbe*>&events)
|
2002-09-16 02:30:33 +02:00
|
|
|
{
|
2013-08-23 05:00:23 +02:00
|
|
|
if (events.size() > 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Events are unaccounted"
|
2002-09-26 03:13:14 +02:00
|
|
|
<< " for in process synthesis." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-20 17:24:28 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetProc::synth_sync: "
|
|
|
|
|
<< "This statement is an async input to a sync process." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
/* Synthesize the input to the DFF. */
|
2014-02-15 23:16:22 +01:00
|
|
|
NetBus accumulated_nex_out (scope, nex_out.pin_count());
|
|
|
|
|
return synth_async(des, scope, nex_map, nex_out, accumulated_nex_out);
|
2002-09-16 02:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2002-06-30 04:21:31 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Async synthesis of assignments is done by synthesizing the rvalue
|
|
|
|
|
* expression, then connecting the l-value directly to the output of
|
|
|
|
|
* the r-value.
|
2002-07-01 02:54:21 +02:00
|
|
|
*
|
|
|
|
|
* The nex_map is the O-set for the statement, and lists the positions
|
|
|
|
|
* of the outputs as the caller wants results linked up. The nex_out,
|
|
|
|
|
* however, is the set of nexa that are to actually get linked to the
|
|
|
|
|
* r-value.
|
2002-06-30 04:21:31 +02:00
|
|
|
*/
|
2002-07-01 02:54:21 +02:00
|
|
|
bool NetAssignBase::synth_async(Design*des, NetScope*scope,
|
2014-02-15 23:16:22 +01:00
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
2014-07-14 03:09:13 +02:00
|
|
|
NetBus&accumulated_nex_out)
|
2002-06-30 04:21:31 +02:00
|
|
|
{
|
2015-06-08 21:27:38 +02:00
|
|
|
/* If the lval is a concatenation, synthesise each part
|
|
|
|
|
separately. */
|
|
|
|
|
if (lval_->more ) {
|
|
|
|
|
/* Temporarily set the lval_ and rval_ fields for each
|
|
|
|
|
part in turn and recurse. Restore them when done. */
|
|
|
|
|
NetAssign_*full_lval = lval_;
|
|
|
|
|
NetExpr*full_rval = rval_;
|
|
|
|
|
unsigned offset = 0;
|
2015-06-09 09:59:28 +02:00
|
|
|
bool flag = true;
|
2015-06-08 21:27:38 +02:00
|
|
|
while (lval_) {
|
|
|
|
|
unsigned width = lval_->lwidth();
|
|
|
|
|
NetEConst*base = new NetEConst(verinum(offset));
|
|
|
|
|
base->set_line(*this);
|
|
|
|
|
rval_ = new NetESelect(full_rval->dup_expr(), base, width);
|
|
|
|
|
rval_->set_line(*this);
|
|
|
|
|
eval_expr(rval_, width);
|
|
|
|
|
NetAssign_*more = lval_->more;
|
|
|
|
|
lval_->more = 0;
|
2015-06-09 09:59:28 +02:00
|
|
|
if (!synth_async(des, scope, nex_map, nex_out, accumulated_nex_out))
|
|
|
|
|
flag = false;
|
2015-06-08 21:27:38 +02:00
|
|
|
lval_ = lval_->more = more;
|
|
|
|
|
offset += width;
|
|
|
|
|
}
|
|
|
|
|
lval_ = full_lval;
|
|
|
|
|
rval_ = full_rval;
|
2015-06-09 09:59:28 +02:00
|
|
|
return flag;
|
2015-06-08 21:27:38 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-22 22:48:34 +01:00
|
|
|
NetNet*rsig = rval_->synthesize(des, scope, rval_);
|
2002-07-01 02:54:21 +02:00
|
|
|
assert(rsig);
|
2002-06-30 04:21:31 +02:00
|
|
|
|
2016-01-30 18:01:13 +01:00
|
|
|
if (lval_->word() && ! dynamic_cast<NetEConst*>(lval_->word())) {
|
|
|
|
|
cerr << get_fileline() << ": sorry: assignment to variable "
|
|
|
|
|
"location in memory is not currently supported in "
|
|
|
|
|
"synthesis." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2002-06-30 04:21:31 +02:00
|
|
|
NetNet*lsig = lval_->sig();
|
2003-12-20 01:59:31 +01:00
|
|
|
if (!lsig) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: "
|
2005-04-25 01:44:01 +02:00
|
|
|
<< "NetAssignBase::synth_async on unsupported lval ";
|
2003-12-20 01:59:31 +01:00
|
|
|
dump_lval(cerr);
|
|
|
|
|
cerr << endl;
|
2007-11-17 03:10:16 +01:00
|
|
|
des->errors += 1;
|
2003-12-20 01:59:31 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2002-06-30 04:21:31 +02:00
|
|
|
|
2005-05-15 06:45:50 +02:00
|
|
|
if (debug_synth2) {
|
2013-09-09 03:18:31 +02:00
|
|
|
cerr << get_fileline() << ": NetAssignBase::synth_async: "
|
|
|
|
|
<< "l-value signal is " << lsig->vector_width() << " bits, "
|
|
|
|
|
<< "r-value signal is " << rsig->vector_width() << " bits." << endl;
|
|
|
|
|
cerr << get_fileline() << ": NetAssignBase::synth_async: "
|
|
|
|
|
<< "lval_->lwidth()=" << lval_->lwidth() << endl;
|
2014-04-25 03:36:49 +02:00
|
|
|
cerr << get_fileline() << ": NetAssignBase::synth_async: "
|
|
|
|
|
<< "lsig = " << scope_path(scope) << "." << lsig->name() << endl;
|
2013-09-09 03:18:31 +02:00
|
|
|
if (const NetExpr*base = lval_->get_base()) {
|
|
|
|
|
cerr << get_fileline() << ": NetAssignBase::synth_async: "
|
|
|
|
|
<< "base_=" << *base << endl;
|
|
|
|
|
}
|
|
|
|
|
cerr << get_fileline() << ": NetAssignBase::synth_async: "
|
|
|
|
|
<< "nex_map.size()==" << nex_map.size()
|
|
|
|
|
<< ", nex_out.pin_count()==" << nex_out.pin_count() << endl;
|
2005-05-15 06:45:50 +02:00
|
|
|
}
|
|
|
|
|
|
2014-05-02 05:37:33 +02:00
|
|
|
// Here we note if the l-value is actually a bit/part
|
|
|
|
|
// select. If so, generate a NetPartSelect to perform the select.
|
2014-07-14 03:09:13 +02:00
|
|
|
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());
|
2013-09-09 03:18:31 +02:00
|
|
|
ivl_assert(*this, lval_->lwidth() < lsig->vector_width());
|
|
|
|
|
|
|
|
|
|
long base_off = 0;
|
2014-05-02 05:37:33 +02:00
|
|
|
|
|
|
|
|
// Evaluate the index expression to a constant.
|
|
|
|
|
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)) {
|
2014-07-14 03:09:13 +02:00
|
|
|
ivl_assert(*this, 0);
|
2013-09-09 03:18:31 +02:00
|
|
|
}
|
|
|
|
|
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(),
|
2014-07-14 03:09:13 +02:00
|
|
|
NetNet::WIRE, NetNet::not_an_array, tmp_type);
|
2013-09-09 03:18:31 +02:00
|
|
|
tmp->local_flag(true);
|
2015-01-15 01:38:57 +01:00
|
|
|
tmp->set_line(*this);
|
2002-07-01 02:54:21 +02:00
|
|
|
|
2013-09-09 03:18:31 +02:00
|
|
|
NetPartSelect*ps = new NetPartSelect(tmp, base_off, lval_->lwidth(), NetPartSelect::PV);
|
|
|
|
|
ps->set_line(*this);
|
|
|
|
|
des->add_node(ps);
|
|
|
|
|
|
|
|
|
|
connect(ps->pin(0), rsig->pin(0));
|
|
|
|
|
rsig = tmp;
|
2014-07-14 03:09:13 +02:00
|
|
|
|
|
|
|
|
} 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);
|
2015-01-15 01:38:57 +01:00
|
|
|
tmp->local_flag(true);
|
|
|
|
|
tmp->set_line(*this);
|
2014-07-14 03:09:13 +02:00
|
|
|
|
|
|
|
|
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;
|
2013-09-09 03:18:31 +02:00
|
|
|
}
|
2014-05-02 05:37:33 +02:00
|
|
|
|
2016-01-30 21:11:58 +01:00
|
|
|
rsig = crop_to_width(des, rsig, lsig->vector_width());
|
|
|
|
|
|
2014-05-04 00:45:15 +02:00
|
|
|
if (nex_out.pin_count() > 1) {
|
|
|
|
|
NexusSet tmp_set;
|
|
|
|
|
nex_output(tmp_set);
|
|
|
|
|
ivl_assert(*this, tmp_set.size()==1);
|
|
|
|
|
unsigned ptr = nex_map.find_nexus(tmp_set[0]);
|
|
|
|
|
ivl_assert(*this, rsig->pin_count()==1);
|
|
|
|
|
ivl_assert(*this, nex_map.size()==nex_out.pin_count());
|
|
|
|
|
ivl_assert(*this, nex_out.pin_count() > ptr);
|
|
|
|
|
connect(nex_out.pin(ptr), rsig->pin(0));
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
ivl_assert(*this, nex_out.pin_count()==1);
|
|
|
|
|
ivl_assert(*this, rsig->pin_count()==1);
|
|
|
|
|
connect(nex_out.pin(0), rsig->pin(0));
|
|
|
|
|
}
|
2002-06-30 04:21:31 +02:00
|
|
|
|
2004-08-28 17:08:31 +02:00
|
|
|
/* This lval_ represents a reg that is a WIRE in the
|
|
|
|
|
synthesized results. This function signals the destructor
|
|
|
|
|
to change the REG that this l-value refers to into a
|
|
|
|
|
WIRE. It is done then, at the last minute, so that pending
|
|
|
|
|
synthesis can continue to work with it as a WIRE. */
|
|
|
|
|
lval_->turn_sig_to_wire_on_release();
|
|
|
|
|
|
2002-06-30 04:21:31 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-17 06:06:01 +02:00
|
|
|
bool NetProc::synth_async_block_substatement_(Design*des, NetScope*scope,
|
|
|
|
|
NexusSet&nex_map,
|
|
|
|
|
NetBus&accumulated_nex_out,
|
|
|
|
|
NetProc*substmt)
|
|
|
|
|
{
|
|
|
|
|
// Create a temporary map of the output only from this statement.
|
|
|
|
|
NexusSet tmp_map;
|
|
|
|
|
substmt->nex_output(tmp_map);
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: "
|
|
|
|
|
<< "tmp_map.size()==" << tmp_map.size()
|
|
|
|
|
<< " for statement at " << substmt->get_fileline()
|
|
|
|
|
<< endl;
|
2014-07-14 03:09:13 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2014-04-17 06:06:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create also a temporary NetBus to collect the
|
|
|
|
|
output from the synthesis. */
|
|
|
|
|
NetBus tmp_out (scope, tmp_map.size());
|
|
|
|
|
|
|
|
|
|
// Map (and move) the accumulated_nex_out for this block
|
|
|
|
|
// 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())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
connect(accumulated_tmp_out.pin(ptr), accumulated_nex_out.pin(idx));
|
|
|
|
|
accumulated_nex_out.pin(idx).unlink();
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-14 03:09:13 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-17 06:06:01 +02:00
|
|
|
bool ok_flag = substmt->synth_async(des, scope, tmp_map, tmp_out, accumulated_tmp_out);
|
|
|
|
|
|
2014-07-14 03:09:13 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-17 06:06:01 +02:00
|
|
|
if (ok_flag == false)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Now map the output from the substatement back to the
|
|
|
|
|
// accumulated_nex_out for this block. Look for the
|
|
|
|
|
// nex_map pin that is linked to the tmp_map.pin(idx)
|
|
|
|
|
// pin, and link that to the tmp_out.pin(idx) output link.
|
|
|
|
|
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());
|
2014-07-14 03:09:13 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: "
|
|
|
|
|
<< "tmp_out.pin(" << idx << "):" << endl;
|
|
|
|
|
tmp_out.pin(idx).dump_link(cerr, 8);
|
|
|
|
|
}
|
2014-04-17 06:06:01 +02:00
|
|
|
connect(accumulated_nex_out.pin(ptr), tmp_out.pin(idx));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-29 02:00:28 +02:00
|
|
|
/*
|
|
|
|
|
* Sequential blocks are translated to asynchronous logic by
|
|
|
|
|
* translating each statement of the block, in order, into gates. The
|
|
|
|
|
* nex_out for the block is the union of the nex_out for all the
|
|
|
|
|
* substatements.
|
|
|
|
|
*/
|
|
|
|
|
bool NetBlock::synth_async(Design*des, NetScope*scope,
|
2014-02-15 23:16:22 +01:00
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
|
|
|
|
NetBus&accumulated_nex_out)
|
2002-07-29 02:00:28 +02:00
|
|
|
{
|
2003-12-17 17:52:39 +01:00
|
|
|
if (last_ == 0) {
|
2002-07-29 02:00:28 +02:00
|
|
|
return true;
|
2003-12-17 17:52:39 +01:00
|
|
|
}
|
2002-07-29 02:00:28 +02:00
|
|
|
|
|
|
|
|
bool flag = true;
|
|
|
|
|
NetProc*cur = last_;
|
|
|
|
|
do {
|
|
|
|
|
cur = cur->next_;
|
|
|
|
|
|
2014-04-17 06:06:01 +02:00
|
|
|
bool ok_flag = synth_async_block_substatement_(des, scope, nex_map,
|
|
|
|
|
accumulated_nex_out,
|
|
|
|
|
cur);
|
2005-04-25 03:35:58 +02:00
|
|
|
flag = flag && ok_flag;
|
2002-07-29 02:00:28 +02:00
|
|
|
if (ok_flag == false)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
} while (cur != last_);
|
|
|
|
|
|
2014-02-15 23:16:22 +01:00
|
|
|
// The output from the block is now the accumulated outputs.
|
|
|
|
|
for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1)
|
|
|
|
|
connect(nex_out.pin(idx), accumulated_nex_out.pin(idx));
|
|
|
|
|
|
2002-07-29 02:00:28 +02:00
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 01:49:21 +01:00
|
|
|
/*
|
|
|
|
|
* This function is used to fix up a MUX selector to be no longer than
|
|
|
|
|
* it needs to be. The general idea is that if the selector needs to
|
|
|
|
|
* be only N bits, but is actually M bits, we translate it to this:
|
|
|
|
|
*
|
|
|
|
|
* osig = { |esig[M-1:N-1], esig[N-2:0] }
|
|
|
|
|
*
|
|
|
|
|
* This obviously implies that (N >= 2) and (M >= N). In the code
|
|
|
|
|
* below, N is sel_need, and M is sel_got (= esig->vector_width()).
|
|
|
|
|
*/
|
|
|
|
|
static NetNet* mux_selector_reduce_width(Design*des, NetScope*scope,
|
|
|
|
|
const LineInfo&loc,
|
|
|
|
|
NetNet*esig, unsigned sel_need)
|
|
|
|
|
{
|
|
|
|
|
const unsigned sel_got = esig->vector_width();
|
|
|
|
|
|
|
|
|
|
ivl_assert(*esig, sel_got >= sel_need);
|
|
|
|
|
|
|
|
|
|
// If the actual width matches the desired width (M==N) then
|
|
|
|
|
// osig is esig itself. We're done.
|
|
|
|
|
if (sel_got == sel_need)
|
|
|
|
|
return esig;
|
|
|
|
|
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << loc.get_fileline() << ": mux_selector_reduce_width: "
|
|
|
|
|
<< "Reduce selector width=" << sel_got
|
|
|
|
|
<< " to " << sel_need << " bits." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivl_assert(*esig, sel_need >= 2);
|
|
|
|
|
|
|
|
|
|
// This is the output signal, osig.
|
|
|
|
|
ivl_variable_type_t osig_data_type = IVL_VT_LOGIC;
|
|
|
|
|
netvector_t*osig_vec = new netvector_t(osig_data_type, sel_need-1, 0);
|
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::TRI, osig_vec);
|
|
|
|
|
osig->local_flag(true);
|
|
|
|
|
osig->set_line(loc);
|
|
|
|
|
|
|
|
|
|
// Create the concat: osig = {...,...}
|
|
|
|
|
NetConcat*osig_cat = new NetConcat(scope, scope->local_symbol(),
|
|
|
|
|
sel_need, 2, true);
|
|
|
|
|
osig_cat->set_line(loc);
|
|
|
|
|
des->add_node(osig_cat);
|
|
|
|
|
connect(osig_cat->pin(0), osig->pin(0));
|
|
|
|
|
|
|
|
|
|
// Create the part select esig[N-2:0]...
|
|
|
|
|
NetPartSelect*ps0 = new NetPartSelect(esig, 0, sel_need-1,
|
|
|
|
|
NetPartSelect::VP);
|
|
|
|
|
ps0->set_line(loc);
|
|
|
|
|
des->add_node(ps0);
|
|
|
|
|
connect(ps0->pin(1), esig->pin(0));
|
|
|
|
|
|
|
|
|
|
netvector_t*ps0_vec = new netvector_t(osig_data_type, sel_need-2, 0);
|
|
|
|
|
NetNet*ps0_sig = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::TRI, ps0_vec);
|
|
|
|
|
ps0_sig->local_flag(true);
|
|
|
|
|
ps0_sig->set_line(loc);
|
|
|
|
|
connect(ps0_sig->pin(0), ps0->pin(0));
|
|
|
|
|
|
|
|
|
|
// osig = {..., esig[N-2:0]}
|
|
|
|
|
connect(osig_cat->pin(1), ps0_sig->pin(0));
|
|
|
|
|
|
|
|
|
|
// Create the part select esig[M-1:N-1]
|
|
|
|
|
NetPartSelect*ps1 = new NetPartSelect(esig, sel_need-1,
|
|
|
|
|
sel_got-sel_need,
|
|
|
|
|
NetPartSelect::VP);
|
|
|
|
|
ps1->set_line(loc);
|
|
|
|
|
des->add_node(ps1);
|
|
|
|
|
connect(ps1->pin(1), esig->pin(0));
|
|
|
|
|
|
|
|
|
|
netvector_t*ps1_vec = new netvector_t(osig_data_type, sel_got-sel_need-1, 0);
|
|
|
|
|
NetNet*ps1_sig = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::TRI, ps1_vec);
|
|
|
|
|
ps1_sig->local_flag(true);
|
|
|
|
|
ps1_sig->set_line(loc);
|
|
|
|
|
connect(ps1_sig->pin(0), ps1->pin(0));
|
|
|
|
|
|
|
|
|
|
// Create the reduction OR: | esig[M-1:N-1]
|
|
|
|
|
NetUReduce*ered = new NetUReduce(scope, scope->local_symbol(),
|
|
|
|
|
NetUReduce::OR, sel_got-sel_need);
|
|
|
|
|
ered->set_line(loc);
|
|
|
|
|
des->add_node(ered);
|
|
|
|
|
connect(ered->pin(1), ps1_sig->pin(0));
|
|
|
|
|
|
|
|
|
|
NetNet*ered_sig = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::TRI, &netvector_t::scalar_logic);
|
|
|
|
|
ered_sig->local_flag(true);
|
|
|
|
|
ered_sig->set_line(loc);
|
|
|
|
|
connect(ered->pin(0), ered_sig->pin(0));
|
|
|
|
|
|
|
|
|
|
// osig = { |esig[M-1:N-1], esig[N-2:0] }
|
|
|
|
|
connect(osig_cat->pin(2), ered_sig->pin(0));
|
|
|
|
|
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-08 00:32:15 +02:00
|
|
|
bool NetCase::synth_async(Design*des, NetScope*scope,
|
2014-02-15 23:16:22 +01:00
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
2014-06-07 19:53:40 +02:00
|
|
|
NetBus&accumulated_nex_out)
|
2002-07-08 00:32:15 +02:00
|
|
|
{
|
2014-06-14 03:22:24 +02:00
|
|
|
if (type()==NetCase::EQZ || type()==NetCase::EQX)
|
|
|
|
|
return synth_async_casez_(des, scope, nex_map, nex_out, accumulated_nex_out);
|
|
|
|
|
|
|
|
|
|
// Special case: If the case expression is constant, then this
|
|
|
|
|
// is a pattern where the guards are non-constant and tested
|
|
|
|
|
// against a constant case. Handle this as chained conditions
|
|
|
|
|
// instead.
|
|
|
|
|
if (dynamic_cast<NetEConst*> (expr_))
|
2014-06-14 03:01:41 +02:00
|
|
|
return synth_async_casez_(des, scope, nex_map, nex_out, accumulated_nex_out);
|
|
|
|
|
|
2014-11-10 01:49:21 +01:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCase::synth_async: "
|
|
|
|
|
<< "Selector expression: " << *expr_ << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-27 06:32:08 +02:00
|
|
|
/* Synthesize the select expression. */
|
2008-12-22 22:48:34 +01:00
|
|
|
NetNet*esig = expr_->synthesize(des, scope, expr_);
|
2002-07-08 00:32:15 +02:00
|
|
|
|
2005-08-27 06:32:08 +02:00
|
|
|
unsigned sel_width = esig->vector_width();
|
2014-06-14 03:01:41 +02:00
|
|
|
ivl_assert(*this, sel_width > 0);
|
2002-07-08 00:32:15 +02:00
|
|
|
|
2014-11-10 01:49:21 +01:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCase::synth_async: "
|
|
|
|
|
<< "selector width (sel_width) = " << sel_width << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-10 02:30:30 +01:00
|
|
|
ivl_assert(*this, nex_map.size() == nex_out.pin_count());
|
2002-07-08 00:32:15 +02:00
|
|
|
|
2014-02-10 02:30:30 +01:00
|
|
|
vector<unsigned> mux_width (nex_out.pin_count());
|
|
|
|
|
for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) {
|
|
|
|
|
mux_width[idx] = nex_map[idx].wid;
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCase::synth_async: "
|
|
|
|
|
<< "idx=" << idx
|
|
|
|
|
<< ", mux_width[idx]=" << mux_width[idx] << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-19 00:39:18 +02:00
|
|
|
|
2014-06-07 19:53:40 +02:00
|
|
|
// The accumulated_nex_out is taken as the input for this
|
|
|
|
|
// statement. Since there are collection of statements that
|
|
|
|
|
// start at this same point, we save all these inputs and
|
|
|
|
|
// reuse them for each statement.
|
|
|
|
|
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));
|
|
|
|
|
}
|
2013-08-19 00:39:18 +02:00
|
|
|
|
2005-08-27 06:32:08 +02:00
|
|
|
/* Collect all the statements into a map of index to
|
|
|
|
|
statement. The guard expression it evaluated to be the
|
|
|
|
|
index of the mux value, and the statement is bound to that
|
|
|
|
|
index. */
|
2002-07-08 00:32:15 +02:00
|
|
|
|
2005-08-27 06:32:08 +02:00
|
|
|
unsigned long max_guard_value = 0;
|
|
|
|
|
map<unsigned long,NetProc*>statement_map;
|
|
|
|
|
NetProc*statement_default = 0;
|
2002-07-08 00:32:15 +02:00
|
|
|
|
2014-06-14 03:01:41 +02:00
|
|
|
for (size_t item = 0 ; item < items_.size() ; item += 1) {
|
2003-03-25 05:04:29 +01:00
|
|
|
if (items_[item].guard == 0) {
|
2005-08-27 06:32:08 +02:00
|
|
|
statement_default = items_[item].statement;
|
2003-03-25 05:04:29 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2002-07-08 00:32:15 +02:00
|
|
|
|
|
|
|
|
NetEConst*ge = dynamic_cast<NetEConst*>(items_[item].guard);
|
2014-11-09 21:11:03 +01:00
|
|
|
if (ge == 0) {
|
|
|
|
|
cerr << items_[item].guard->get_fileline() << ": sorry: "
|
|
|
|
|
<< "variable case item expressions with a variable "
|
|
|
|
|
<< "case select expression are not supported in "
|
|
|
|
|
<< "synthesis. " << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-06-14 03:22:24 +02:00
|
|
|
ivl_assert(*this, ge);
|
2002-07-08 00:32:15 +02:00
|
|
|
verinum gval = ge->value();
|
2005-08-27 06:32:08 +02:00
|
|
|
|
2014-11-09 21:11:03 +01:00
|
|
|
unsigned long sel_idx = gval.as_ulong();
|
2002-07-08 00:32:15 +02:00
|
|
|
|
2014-11-09 21:11:03 +01:00
|
|
|
if (statement_map[sel_idx]) {
|
|
|
|
|
cerr << ge->get_fileline() << ": warning: duplicate case "
|
|
|
|
|
<< "value '" << sel_idx << "' detected. This case is "
|
|
|
|
|
<< "unreachable." << endl;
|
|
|
|
|
delete items_[item].statement;
|
|
|
|
|
items_[item].statement = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2005-08-27 06:32:08 +02:00
|
|
|
|
|
|
|
|
if (sel_idx > max_guard_value)
|
|
|
|
|
max_guard_value = sel_idx;
|
2014-11-14 01:26:15 +01:00
|
|
|
|
2014-11-09 21:11:03 +01:00
|
|
|
ivl_assert(*this, items_[item].statement);
|
|
|
|
|
statement_map[sel_idx] = items_[item].statement;
|
2003-03-25 05:04:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-12-07 13:10:15 +01:00
|
|
|
// The minimum selector width is the number of inputs that
|
|
|
|
|
// are selected, rounded up to the nearest power of 2.
|
2016-07-23 00:09:36 +02:00
|
|
|
unsigned sel_need = max(ceil(log2(max_guard_value + 1)), 1.0);
|
2003-03-25 05:04:29 +01:00
|
|
|
|
2014-01-29 00:50:27 +01:00
|
|
|
// If the sel_width can select more than just the explicit
|
2013-08-17 05:54:30 +02:00
|
|
|
// guard values, and there is a default statement, then adjust
|
2014-12-07 13:10:15 +01:00
|
|
|
// the sel_need to allow for the implicit selections.
|
|
|
|
|
if (statement_default && (sel_width > sel_need))
|
2014-11-10 01:49:21 +01:00
|
|
|
sel_need += 1;
|
2014-12-07 13:10:15 +01:00
|
|
|
|
|
|
|
|
// The mux size is always an exact power of 2.
|
|
|
|
|
if (sel_need >= 8*sizeof(unsigned)) {
|
|
|
|
|
cerr << get_fileline() << ": sorry: mux select width of "
|
|
|
|
|
<< sel_need << " bits is too large for synthesis." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
2013-08-17 05:54:30 +02:00
|
|
|
}
|
2014-12-07 13:10:15 +01:00
|
|
|
unsigned mux_size = 1U << sel_need;
|
2013-08-17 05:54:30 +02:00
|
|
|
|
2014-11-10 01:49:21 +01:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCase::synth_async: "
|
|
|
|
|
<< "Adjusted mux_size is " << mux_size
|
|
|
|
|
<< " (max_guard_value=" << max_guard_value
|
|
|
|
|
<< ", sel_need=" << sel_need
|
|
|
|
|
<< ", sel_width=" << sel_width << ")." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sel_width > sel_need) {
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCase::synth_async: "
|
|
|
|
|
<< "Selector is " << sel_width << " bits, "
|
|
|
|
|
<< "need only " << sel_need << " bits." << endl;
|
|
|
|
|
}
|
|
|
|
|
esig = mux_selector_reduce_width(des, scope, *this, esig, sel_need);
|
|
|
|
|
}
|
2013-08-17 05:54:30 +02:00
|
|
|
|
2014-11-09 21:11:03 +01:00
|
|
|
if (!statement_default && (statement_map.size() != ((size_t)1 << sel_width))) {
|
|
|
|
|
cerr << get_fileline() << ": sorry: Latch inferred from "
|
|
|
|
|
<< "incomplete case statement. This is not supported "
|
|
|
|
|
<< "in synthesis." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2003-03-25 05:04:29 +01:00
|
|
|
|
2014-06-07 19:53:40 +02:00
|
|
|
/* If there is a default clause, synthesize it once and we'll
|
2013-08-17 05:54:30 +02:00
|
|
|
link it in wherever it is needed. */
|
2014-02-10 02:30:30 +01:00
|
|
|
NetBus default_bus (scope, nex_map.size());
|
2013-08-17 05:54:30 +02:00
|
|
|
if (statement_default) {
|
|
|
|
|
|
2014-06-07 19:53:40 +02:00
|
|
|
bool flag = synth_async_block_substatement_(des, scope, nex_map,
|
|
|
|
|
accumulated_nex_out,
|
|
|
|
|
statement_default);
|
|
|
|
|
if (!flag) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < default_bus.pin_count() ; idx += 1) {
|
|
|
|
|
connect(default_bus.pin(idx), accumulated_nex_out.pin(idx));
|
|
|
|
|
accumulated_nex_out.pin(idx).unlink();
|
|
|
|
|
}
|
2013-08-18 01:33:46 +02:00
|
|
|
|
2014-06-07 19:53:40 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCase::synth_async: "
|
|
|
|
|
<< "synthesize default clause at " << statement_default->get_fileline()
|
|
|
|
|
<< " is done." << endl;
|
|
|
|
|
}
|
2014-02-10 02:30:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<NetMux*> mux (mux_width.size());
|
|
|
|
|
for (size_t mdx = 0 ; mdx < mux_width.size() ; mdx += 1) {
|
|
|
|
|
mux[mdx] = new NetMux(scope, scope->local_symbol(),
|
2014-11-10 01:49:21 +01:00
|
|
|
mux_width[mdx], mux_size, sel_need);
|
2014-02-10 02:30:30 +01:00
|
|
|
des->add_node(mux[mdx]);
|
|
|
|
|
|
|
|
|
|
// The select signal is already synthesized, and is
|
|
|
|
|
// common for every mux of this case statement. Simply
|
|
|
|
|
// hook it up.
|
|
|
|
|
connect(mux[mdx]->pin_Sel(), esig->pin(0));
|
|
|
|
|
|
|
|
|
|
// The outputs are in the nex_out, and connected to the
|
|
|
|
|
// mux Result pins.
|
|
|
|
|
connect(mux[mdx]->pin_Result(), nex_out.pin(mdx));
|
|
|
|
|
|
|
|
|
|
// Make sure the output is now connected to a net. If
|
|
|
|
|
// not, then create a fake one to carry the net-ness of
|
|
|
|
|
// the pin.
|
|
|
|
|
if (mux[mdx]->pin_Result().nexus()->pick_any_net() == 0) {
|
|
|
|
|
ivl_variable_type_t mux_data_type = IVL_VT_LOGIC;
|
|
|
|
|
netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0);
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::TRI, tmp_vec);
|
|
|
|
|
tmp->local_flag(true);
|
|
|
|
|
ivl_assert(*this, tmp->vector_width() != 0);
|
|
|
|
|
connect(mux[mdx]->pin_Result(), tmp->pin(0));
|
|
|
|
|
}
|
2013-08-17 05:54:30 +02:00
|
|
|
}
|
2005-08-27 06:32:08 +02:00
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < mux_size ; idx += 1) {
|
|
|
|
|
|
|
|
|
|
NetProc*stmt = statement_map[idx];
|
2014-02-10 02:30:30 +01:00
|
|
|
if (stmt==0 && statement_default) {
|
2014-06-07 19:53:40 +02:00
|
|
|
ivl_assert(*this, default_bus.pin_count() == mux.size());
|
2014-02-10 02:30:30 +01:00
|
|
|
for (size_t mdx = 0 ; mdx < mux.size() ; mdx += 1)
|
2014-06-07 19:53:40 +02:00
|
|
|
connect(mux[mdx]->pin_Data(idx), default_bus.pin(mdx));
|
2014-02-10 02:30:30 +01:00
|
|
|
|
2013-08-17 05:54:30 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2014-11-09 21:11:03 +01:00
|
|
|
ivl_assert(*this, stmt);
|
2002-07-08 00:32:15 +02:00
|
|
|
|
2014-02-15 23:16:22 +01:00
|
|
|
NetBus accumulated_tmp (scope, nex_map.size());
|
2014-06-07 19:53:40 +02:00
|
|
|
for (unsigned pin = 0 ; pin < nex_map.size() ; pin += 1)
|
|
|
|
|
connect(accumulated_tmp.pin(pin), statement_input.pin(pin));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
synth_async_block_substatement_(des, scope, nex_map, accumulated_tmp, stmt);
|
2013-08-18 01:33:46 +02:00
|
|
|
|
2014-02-10 02:30:30 +01:00
|
|
|
for (size_t mdx = 0 ; mdx < mux.size() ; mdx += 1) {
|
2014-06-07 19:53:40 +02:00
|
|
|
connect(mux[mdx]->pin_Data(idx), accumulated_tmp.pin(mdx));
|
2014-02-10 02:30:30 +01:00
|
|
|
|
|
|
|
|
if (mux[mdx]->pin_Data(idx).nexus()->pick_any_net()==0) {
|
|
|
|
|
cerr << get_fileline() << ": warning: case " << idx
|
2014-06-07 19:53:40 +02:00
|
|
|
<< " has no input for mux slice " << mdx << "." << endl;
|
2014-02-10 02:30:30 +01:00
|
|
|
|
|
|
|
|
ivl_variable_type_t mux_data_type = IVL_VT_LOGIC;
|
|
|
|
|
netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0);
|
|
|
|
|
NetNet*tmpn = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::TRI, tmp_vec);
|
|
|
|
|
tmpn->local_flag(true);
|
|
|
|
|
ivl_assert(*this, tmpn->vector_width() != 0);
|
|
|
|
|
connect(mux[mdx]->pin_Data(idx), tmpn->pin(0));
|
|
|
|
|
}
|
|
|
|
|
ivl_assert(*this, mux[mdx]->pin_Data(idx).nexus()->pick_any_net());
|
|
|
|
|
}
|
2005-08-27 06:32:08 +02:00
|
|
|
}
|
2002-07-08 00:32:15 +02:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-14 03:01:41 +02:00
|
|
|
/*
|
|
|
|
|
* casez statements are hard to implement as a single wide mux because
|
|
|
|
|
* the test doesn't really map to a select input. Instead, implement
|
|
|
|
|
* it as a chain of binary muxes. This gives the synthesizer my
|
|
|
|
|
* flexibility, and is more typically what is desired from a casez anyhow.
|
|
|
|
|
*/
|
|
|
|
|
bool NetCase::synth_async_casez_(Design*des, NetScope*scope,
|
|
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
|
|
|
|
NetBus&accumulated_nex_out)
|
|
|
|
|
{
|
|
|
|
|
/* Synthesize the select expression. */
|
|
|
|
|
NetNet*esig = expr_->synthesize(des, scope, expr_);
|
|
|
|
|
|
|
|
|
|
unsigned sel_width = esig->vector_width();
|
|
|
|
|
ivl_assert(*this, sel_width > 0);
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, nex_map.size() == nex_out.pin_count());
|
|
|
|
|
|
2014-07-20 02:22:33 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) {
|
|
|
|
|
cerr << get_fileline() << ": NetCase::synth_async_casez_: "
|
|
|
|
|
<< "nex_out.pin(" << idx << "):" << endl;
|
|
|
|
|
nex_out.pin(idx).dump_link(cerr, 8);
|
|
|
|
|
}
|
|
|
|
|
for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) {
|
|
|
|
|
cerr << get_fileline() << ": NetCase::synth_async_casez_: "
|
|
|
|
|
<< "accumulated_nex_out.pin(" << idx << "):" << endl;
|
|
|
|
|
accumulated_nex_out.pin(idx).dump_link(cerr, 8);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-14 03:01:41 +02:00
|
|
|
vector<unsigned>mux_width (nex_out.pin_count());
|
|
|
|
|
for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) {
|
|
|
|
|
mux_width[idx] = nex_map[idx].wid;
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCase::synth_async_casez_: "
|
|
|
|
|
<< "idx=" << idx
|
|
|
|
|
<< ", mux_width[idx]=" << mux_width[idx] << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The accumulated_nex_out is taken as the input for this
|
|
|
|
|
// statement. Since there are collection of statements that
|
|
|
|
|
// start at this same point, we save all these inputs and
|
|
|
|
|
// reuse them for each statement.
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Look for a default statement.
|
|
|
|
|
NetProc*statement_default = 0;
|
|
|
|
|
for (size_t item = 0 ; item < items_.size() ; item += 1) {
|
|
|
|
|
if (items_[item].guard != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, statement_default==0);
|
|
|
|
|
statement_default = items_[item].statement;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetBus default_bus (scope, nex_out.pin_count());
|
|
|
|
|
if (statement_default) {
|
|
|
|
|
bool flag = synth_async_block_substatement_(des, scope, nex_map,
|
|
|
|
|
accumulated_nex_out,
|
|
|
|
|
statement_default);
|
|
|
|
|
if (!flag) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < default_bus.pin_count() ; idx += 1) {
|
|
|
|
|
connect(default_bus.pin(idx), accumulated_nex_out.pin(idx));
|
|
|
|
|
accumulated_nex_out.pin(idx).unlink();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCase::synth_async_casez_: "
|
|
|
|
|
<< "synthesize default clause at " << statement_default->get_fileline()
|
|
|
|
|
<< " is done." << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
netvector_t*condit_type = new netvector_t(IVL_VT_LOGIC, 0, 0);
|
|
|
|
|
|
2015-05-04 23:32:29 +02:00
|
|
|
NetCaseCmp::kind_t case_kind = NetCaseCmp::EEQ;
|
2014-06-14 03:01:41 +02:00
|
|
|
switch (type()) {
|
|
|
|
|
case NetCase::EQ:
|
|
|
|
|
case_kind = NetCaseCmp::EEQ;
|
|
|
|
|
break;
|
|
|
|
|
case NetCase::EQX:
|
|
|
|
|
case_kind = NetCaseCmp::XEQ;
|
|
|
|
|
break;
|
|
|
|
|
case NetCase::EQZ:
|
|
|
|
|
case_kind = NetCaseCmp::ZEQ;
|
|
|
|
|
break;
|
2014-06-17 19:46:22 +02:00
|
|
|
default:
|
|
|
|
|
assert(0);
|
2014-06-14 03:01:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the items from last to first. We generate a
|
2014-07-20 02:22:33 +02:00
|
|
|
// true/false mux, with the select being the comparison of
|
2014-06-14 03:01:41 +02:00
|
|
|
// the case select with the guard expression. The true input
|
|
|
|
|
// (data1) is the current statement, and the false input is
|
|
|
|
|
// the result of a later statement.
|
|
|
|
|
vector<NetMux*>mux_prev (mux_width.size());
|
|
|
|
|
for (size_t idx = 0 ; idx < items_.size() ; idx += 1) {
|
|
|
|
|
size_t item = items_.size()-idx-1;
|
|
|
|
|
if (items_[item].guard == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
NetProc*stmt = items_[item].statement;
|
|
|
|
|
ivl_assert(*this, stmt);
|
|
|
|
|
|
|
|
|
|
NetExpr*guard_expr = items_[item].guard;
|
|
|
|
|
NetNet*guard = guard_expr->synthesize(des, scope, guard_expr);
|
|
|
|
|
|
|
|
|
|
NetCaseCmp*condit_dev = new NetCaseCmp(scope, scope->local_symbol(),
|
|
|
|
|
sel_width, case_kind);
|
|
|
|
|
des->add_node(condit_dev);
|
|
|
|
|
condit_dev->set_line(*this);
|
2015-06-02 19:40:24 +02:00
|
|
|
// Note that the expression that may have wildcards must
|
|
|
|
|
// go in the pin(2) input. This is the definition of the
|
2014-06-14 03:01:41 +02:00
|
|
|
// NetCaseCmp statement.
|
2014-06-16 18:45:03 +02:00
|
|
|
connect(condit_dev->pin(1), esig->pin(0));
|
|
|
|
|
connect(condit_dev->pin(2), guard->pin(0));
|
2014-06-14 03:01:41 +02:00
|
|
|
|
|
|
|
|
NetNet*condit = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::TRI, condit_type);
|
|
|
|
|
condit->set_line(*this);
|
|
|
|
|
condit->local_flag(true);
|
|
|
|
|
connect(condit_dev->pin(0), condit->pin(0));
|
|
|
|
|
|
|
|
|
|
// Synthesize the guarded statement.
|
|
|
|
|
NetBus true_bus (scope, nex_out.pin_count());
|
|
|
|
|
for (unsigned pin = 0 ; pin < nex_map.size() ; pin += 1)
|
|
|
|
|
connect(true_bus.pin(pin), statement_input.pin(pin));
|
|
|
|
|
|
|
|
|
|
synth_async_block_substatement_(des, scope, nex_map, true_bus, stmt);
|
|
|
|
|
|
|
|
|
|
for (unsigned mdx = 0 ; mdx < mux_width.size() ; mdx += 1) {
|
|
|
|
|
NetMux*mux_cur = new NetMux(scope, scope->local_symbol(),
|
|
|
|
|
mux_width[mdx], 2, 1);
|
|
|
|
|
des->add_node(mux_cur);
|
|
|
|
|
mux_cur->set_line(*this);
|
|
|
|
|
connect(mux_cur->pin_Sel(), condit->pin(0));
|
|
|
|
|
|
|
|
|
|
connect(mux_cur->pin_Data(1), true_bus.pin(mdx));
|
|
|
|
|
|
|
|
|
|
// If there is a previous mux, then use that as the
|
2014-07-20 02:22:33 +02:00
|
|
|
// false clause input. Otherwise, use the
|
|
|
|
|
// default. But wait, if there is no default, then
|
|
|
|
|
// use the accumulated input.
|
2014-06-14 03:01:41 +02:00
|
|
|
if (mux_prev[mdx]) {
|
|
|
|
|
connect(mux_cur->pin_Data(0), mux_prev[mdx]->pin_Result());
|
2014-07-20 02:22:33 +02:00
|
|
|
} else if (default_bus.pin(mdx).is_linked()) {
|
2014-06-14 03:01:41 +02:00
|
|
|
connect(mux_cur->pin_Data(0), default_bus.pin(mdx));
|
2014-07-20 02:22:33 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
connect(mux_cur->pin_Data(0), statement_input.pin(mdx));
|
2014-06-14 03:01:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make a NetNet for the result.
|
|
|
|
|
ivl_variable_type_t mux_data_type = IVL_VT_LOGIC;
|
|
|
|
|
netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0);
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::TRI, tmp_vec);
|
|
|
|
|
tmp->local_flag(true);
|
2015-01-15 01:38:57 +01:00
|
|
|
tmp->set_line(*this);
|
2014-06-14 03:01:41 +02:00
|
|
|
ivl_assert(*this, tmp->vector_width() != 0);
|
|
|
|
|
connect(mux_cur->pin_Result(), tmp->pin(0));
|
|
|
|
|
|
|
|
|
|
// This mux becomes the "false" input to the next mux.
|
|
|
|
|
mux_prev[mdx] = mux_cur;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Connect the last mux to the output.
|
|
|
|
|
for (size_t mdx = 0 ; mdx < mux_prev.size() ; mdx += 1) {
|
|
|
|
|
connect(mux_prev[mdx]->pin_Result(), nex_out.pin(mdx));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-15 23:16:22 +01:00
|
|
|
/*
|
|
|
|
|
* A condit statement (if (cond) ... else ... ;) infers an A-B mux,
|
|
|
|
|
* with the cond expression acting as a select input. If the cond
|
|
|
|
|
* expression is true, the if_ clause is selected, and if false, the
|
|
|
|
|
* else_ clause is selected.
|
|
|
|
|
*/
|
2013-08-12 04:10:50 +02:00
|
|
|
bool NetCondit::synth_async(Design*des, NetScope*scope,
|
2014-02-15 23:16:22 +01:00
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
|
|
|
|
NetBus&accumulated_nex_out)
|
2002-06-30 04:21:31 +02:00
|
|
|
{
|
2003-12-17 17:52:39 +01:00
|
|
|
if (if_ == 0) {
|
2002-10-20 21:19:37 +02:00
|
|
|
return false;
|
2003-12-17 17:52:39 +01:00
|
|
|
}
|
2002-10-20 21:19:37 +02:00
|
|
|
if (else_ == 0) {
|
2014-02-15 23:54:58 +01:00
|
|
|
bool latch_flag = false;
|
|
|
|
|
for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) {
|
|
|
|
|
if (! accumulated_nex_out.pin(idx).is_linked())
|
|
|
|
|
latch_flag = true;
|
|
|
|
|
}
|
|
|
|
|
if (latch_flag) {
|
|
|
|
|
cerr << get_fileline() << ": error: Asynchronous if statement"
|
|
|
|
|
<< " cannot synthesize missing \"else\""
|
|
|
|
|
<< " without generating latches." << endl;
|
2014-07-14 03:09:13 +02:00
|
|
|
//return false;
|
2014-02-15 23:54:58 +01:00
|
|
|
}
|
2002-10-20 21:19:37 +02:00
|
|
|
}
|
|
|
|
|
|
2014-02-15 23:16:22 +01:00
|
|
|
ivl_assert(*this, if_ != 0);
|
2013-08-12 04:10:50 +02:00
|
|
|
// Synthesize the condition. This will act as a select signal
|
|
|
|
|
// for a binary mux.
|
|
|
|
|
NetNet*ssig = expr_->synthesize(des, scope, expr_);
|
|
|
|
|
assert(ssig);
|
|
|
|
|
|
2014-04-17 06:06:01 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCondit::synth_async: "
|
|
|
|
|
<< "Synthesize if clause at " << if_->get_fileline()
|
|
|
|
|
<< endl;
|
2014-07-14 03:09:13 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2014-04-17 06:06:01 +02:00
|
|
|
}
|
|
|
|
|
|
2014-05-26 03:33:26 +02:00
|
|
|
NetBus statement_input (scope, nex_out.pin_count());
|
2014-07-14 03:09:13 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-05-26 03:33:26 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
bool flag;
|
2013-08-16 04:23:36 +02:00
|
|
|
NetBus asig(scope, nex_out.pin_count());
|
2014-07-14 03:09:13 +02:00
|
|
|
flag = if_->synth_async(des, scope, nex_map, asig, accumulated_nex_out);
|
2003-12-20 01:59:31 +01:00
|
|
|
if (!flag) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2014-02-15 23:16:22 +01:00
|
|
|
NetBus btmp(scope, nex_out.pin_count());
|
2013-08-16 04:23:36 +02:00
|
|
|
NetBus bsig(scope, nex_out.pin_count());
|
2014-02-15 23:54:58 +01:00
|
|
|
|
|
|
|
|
if (else_==0) {
|
2014-02-15 23:16:22 +01:00
|
|
|
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();
|
|
|
|
|
}
|
2014-04-17 06:06:01 +02:00
|
|
|
|
2014-02-15 23:54:58 +01:00
|
|
|
} else {
|
2014-04-17 06:06:01 +02:00
|
|
|
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCondit::synth_async: "
|
|
|
|
|
<< "Synthesize else clause at " << else_->get_fileline()
|
|
|
|
|
<< endl;
|
2014-07-14 03:09:13 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2014-04-17 06:06:01 +02:00
|
|
|
}
|
|
|
|
|
|
2014-07-14 03:09:13 +02:00
|
|
|
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));
|
2014-02-15 23:54:58 +01:00
|
|
|
}
|
2014-07-14 03:09:13 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2014-04-17 06:06:01 +02:00
|
|
|
}
|
2014-04-25 03:36:49 +02:00
|
|
|
|
2014-07-14 03:09:13 +02:00
|
|
|
flag = synth_async_block_substatement_(des, scope, nex_map, accumulated_btmp_out, else_);
|
|
|
|
|
if (!flag) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-04-25 03:36:49 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCondit::synth_async: "
|
|
|
|
|
<< "synthesize else clause at " << else_->get_fileline()
|
|
|
|
|
<< " is done." << endl;
|
2014-07-14 03:09:13 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2014-04-25 03:36:49 +02:00
|
|
|
}
|
2014-07-14 03:09:13 +02:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
}
|
2002-07-01 02:54:21 +02:00
|
|
|
|
2014-05-25 05:08:48 +02:00
|
|
|
/* The nex_out output, asig input, and bsig input all have the
|
|
|
|
|
same pin count (usually, but not always 1) because they are
|
|
|
|
|
net arrays of the same dimension. The for loop below creates
|
|
|
|
|
a NetMux for each pin of the output. (Note that pins may
|
|
|
|
|
be, in fact usually are, vectors.) */
|
|
|
|
|
|
2013-08-16 04:23:36 +02:00
|
|
|
ivl_assert(*this, nex_out.pin_count()==asig.pin_count());
|
|
|
|
|
ivl_assert(*this, nex_out.pin_count()==bsig.pin_count());
|
2013-08-12 04:10:50 +02:00
|
|
|
|
2014-04-17 06:06:01 +02:00
|
|
|
bool rc_flag = true;
|
2013-08-16 04:23:36 +02:00
|
|
|
for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) {
|
2014-06-13 02:50:44 +02:00
|
|
|
// It should not be possible for the a (true) or b
|
|
|
|
|
// (false) signals to be missing. If either is, print a
|
|
|
|
|
// warning and clear a flag so that the rest of this
|
|
|
|
|
// code can find a way to cope.
|
|
|
|
|
bool asig_is_present = true;
|
|
|
|
|
if (! asig.pin(idx).nexus()->pick_any_net()) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< "True clause of conditional statement might not"
|
|
|
|
|
<< " drive all expected outputs." << endl;
|
|
|
|
|
asig_is_present = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bsig_is_present = true;
|
|
|
|
|
if (! bsig.pin(idx).nexus()->pick_any_net()) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< "False clause of conditional statement might not"
|
|
|
|
|
<< " drive all expected outputs." << endl;
|
|
|
|
|
bsig_is_present = false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-18 01:33:46 +02:00
|
|
|
// Guess the mux type from the type of the output.
|
|
|
|
|
ivl_variable_type_t mux_data_type = IVL_VT_LOGIC;
|
|
|
|
|
if (NetNet*tmp = nex_out.pin(idx).nexus()->pick_any_net()) {
|
|
|
|
|
mux_data_type = tmp->data_type();
|
|
|
|
|
}
|
2014-05-25 05:08:48 +02:00
|
|
|
|
|
|
|
|
unsigned mux_off = 0;
|
2014-06-13 02:50:44 +02:00
|
|
|
unsigned mux_width;
|
|
|
|
|
if (asig_is_present)
|
|
|
|
|
mux_width = asig.pin(idx).nexus()->vector_width();
|
|
|
|
|
else if (bsig_is_present)
|
|
|
|
|
mux_width = bsig.pin(idx).nexus()->vector_width();
|
|
|
|
|
else
|
|
|
|
|
mux_width = 0;
|
|
|
|
|
|
2014-07-14 03:09:13 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2014-05-25 05:08:48 +02:00
|
|
|
|
|
|
|
|
NetPartSelect*apv = detect_partselect_lval(asig.pin(idx));
|
|
|
|
|
if (debug_synth2 && apv) {
|
|
|
|
|
cerr << get_fileline() << ": NetCondit::synth_async: "
|
|
|
|
|
<< "Assign-to-part apv base=" << apv->base()
|
|
|
|
|
<< ", width=" << apv->width() << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetPartSelect*bpv = detect_partselect_lval(bsig.pin(idx));
|
|
|
|
|
if (debug_synth2 && bpv) {
|
|
|
|
|
cerr << get_fileline() << ": NetCondit::synth_async: "
|
|
|
|
|
<< "Assign-to-part bpv base=" << bpv->base()
|
|
|
|
|
<< ", width=" << bpv->width() << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-14 03:09:13 +02:00
|
|
|
unsigned mux_lwidth = mux_width;
|
|
|
|
|
ivl_assert(*this, mux_width != 0);
|
|
|
|
|
|
2014-05-25 05:08:48 +02:00
|
|
|
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
|
|
|
|
|
// create a much narrower mux that only
|
|
|
|
|
// manipulates the width of the part.
|
|
|
|
|
mux_width = apv->width();
|
|
|
|
|
mux_off = apv->base();
|
|
|
|
|
asig.pin(idx).unlink();
|
|
|
|
|
bsig.pin(idx).unlink();
|
|
|
|
|
connect(asig.pin(idx), apv->pin(0));
|
|
|
|
|
connect(bsig.pin(idx), bpv->pin(0));
|
|
|
|
|
delete apv;
|
|
|
|
|
delete bpv;
|
|
|
|
|
} else {
|
|
|
|
|
// The part selects are of no use. Forget them.
|
|
|
|
|
apv = 0;
|
|
|
|
|
bpv = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-13 02:50:44 +02:00
|
|
|
if (bsig_is_present && mux_width != bsig.pin(idx).nexus()->vector_width()) {
|
2014-04-17 06:06:01 +02:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "NetCondit::synth_async: "
|
|
|
|
|
<< "Mux input sizes do not match."
|
2014-05-25 05:08:48 +02:00
|
|
|
<< " A size=" << mux_lwidth
|
2014-04-17 06:06:01 +02:00
|
|
|
<< ", B size=" << bsig.pin(idx).nexus()->vector_width()
|
|
|
|
|
<< endl;
|
2014-04-25 03:36:49 +02:00
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
<< "asig node pins:" << endl;
|
|
|
|
|
asig.dump_node_pins(cerr, 8);
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
<< "if_ statement:" << endl;
|
|
|
|
|
if_->dump(cerr, 8);
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
<< "bsig node pins:" << endl;
|
|
|
|
|
bsig.dump_node_pins(cerr, 4);
|
|
|
|
|
if (else_) {
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
<< "else_ statement:" << endl;
|
|
|
|
|
else_->dump(cerr, 8);
|
|
|
|
|
}
|
2014-04-17 06:06:01 +02:00
|
|
|
rc_flag = false;
|
|
|
|
|
}
|
2013-08-16 04:23:36 +02:00
|
|
|
|
|
|
|
|
NetMux*mux = new NetMux(scope, scope->local_symbol(),
|
|
|
|
|
mux_width, 2, 1);
|
2014-06-13 02:50:44 +02:00
|
|
|
mux->set_line(*this);
|
2013-08-16 04:23:36 +02:00
|
|
|
|
2014-06-13 02:50:44 +02:00
|
|
|
netvector_t*tmp_type = 0;
|
2013-08-16 04:23:36 +02:00
|
|
|
if (mux_width==1)
|
|
|
|
|
tmp_type = new netvector_t(mux_data_type);
|
|
|
|
|
else
|
|
|
|
|
tmp_type = new netvector_t(mux_data_type, mux_width-1,0);
|
|
|
|
|
|
|
|
|
|
// Bind some temporary signals to carry pin type.
|
|
|
|
|
NetNet*otmp = new NetNet(scope, scope->local_symbol(),
|
2014-07-14 03:09:13 +02:00
|
|
|
NetNet::WIRE, NetNet::not_an_array, tmp_type);
|
2013-08-16 04:23:36 +02:00
|
|
|
otmp->local_flag(true);
|
2015-01-15 01:38:57 +01:00
|
|
|
otmp->set_line(*this);
|
2014-05-25 05:08:48 +02:00
|
|
|
connect(mux->pin_Result(),otmp->pin(0));
|
2013-08-16 04:23:36 +02:00
|
|
|
|
|
|
|
|
connect(mux->pin_Sel(), ssig->pin(0));
|
|
|
|
|
connect(mux->pin_Data(1), asig.pin(idx));
|
|
|
|
|
connect(mux->pin_Data(0), bsig.pin(idx));
|
2014-05-25 05:08:48 +02:00
|
|
|
|
2014-06-13 02:50:44 +02:00
|
|
|
if (! asig_is_present) {
|
|
|
|
|
tmp_type = new netvector_t(mux_data_type, mux_width-1,0);
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2014-07-14 03:09:13 +02:00
|
|
|
NetNet::WIRE, NetNet::not_an_array, tmp_type);
|
2015-01-15 01:38:57 +01:00
|
|
|
tmp->local_flag(true);
|
|
|
|
|
tmp->set_line(*this);
|
2014-06-13 02:50:44 +02:00
|
|
|
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(),
|
2014-07-14 03:09:13 +02:00
|
|
|
NetNet::WIRE, NetNet::not_an_array, tmp_type);
|
2015-01-15 01:38:57 +01:00
|
|
|
tmp->local_flag(true);
|
|
|
|
|
tmp->set_line(*this);
|
2014-06-13 02:50:44 +02:00
|
|
|
connect(mux->pin_Data(0), tmp->pin(0));
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-25 05:08:48 +02:00
|
|
|
// We are only muxing a part of the output vector, so
|
|
|
|
|
// make a NetPartSelect::PV to widen the vector to the
|
|
|
|
|
// output at hand.
|
|
|
|
|
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(),
|
2014-07-14 03:09:13 +02:00
|
|
|
NetNet::WIRE, NetNet::not_an_array, tmp_type);
|
2015-01-15 01:38:57 +01:00
|
|
|
tmp->local_flag(true);
|
|
|
|
|
tmp->set_line(*this);
|
2014-05-25 05:08:48 +02:00
|
|
|
NetPartSelect*ps = new NetPartSelect(tmp, mux_off, mux_width, NetPartSelect::PV);
|
|
|
|
|
des->add_node(ps);
|
|
|
|
|
connect(ps->pin(0), otmp->pin(0));
|
|
|
|
|
otmp = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connect(nex_out.pin(idx), otmp->pin(0));
|
2013-08-16 04:23:36 +02:00
|
|
|
|
2014-05-26 03:33:26 +02:00
|
|
|
// Handle the special case that this NetMux is only
|
|
|
|
|
// assigning to a part of the vector. If that is the
|
|
|
|
|
// case, then we need to blend this output with the
|
|
|
|
|
// already calculated input to this statement so that we
|
|
|
|
|
// don't accidentally disconnect the other drivers to
|
|
|
|
|
// other bits.
|
|
|
|
|
// FIXME: NEED TO CHECK THAT THESE DRIVERS DON'T
|
|
|
|
|
// OVERLAP. THIS CODE CURRENTLY DOESN'T DO THAT TEST.
|
|
|
|
|
if (mux_width < mux_lwidth && if_ && else_) {
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetCondit::synth_async: "
|
2014-07-14 03:09:13 +02:00
|
|
|
<< "This NetMux only impacts a few bits of output,"
|
2014-05-26 03:33:26 +02:00
|
|
|
<< " so combine nex_out with statement input."
|
|
|
|
|
<< endl;
|
|
|
|
|
cerr << get_fileline() << ": NetCondit::synth_async: "
|
|
|
|
|
<< "MISSING TEST FOR CORRECTNESS OF THE BLEND!"
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
2014-06-03 00:42:02 +02:00
|
|
|
vector<bool>mask = statement_input.pin(idx).nexus()->driven_mask();
|
2015-01-15 00:10:15 +01:00
|
|
|
// If the mask is empty then there are no bits in the
|
|
|
|
|
// nexus to check yet.
|
|
|
|
|
if (! mask.empty()) {
|
|
|
|
|
for (size_t bit = mux_off;
|
|
|
|
|
bit < mux_off+mux_width;
|
|
|
|
|
bit += 1) {
|
|
|
|
|
ivl_assert(*this, mask[bit]==false);
|
|
|
|
|
}
|
2014-06-03 00:42:02 +02:00
|
|
|
}
|
2014-05-26 03:33:26 +02:00
|
|
|
connect(nex_out.pin(idx), statement_input.pin(idx));
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-16 04:23:36 +02:00
|
|
|
des->add_node(mux);
|
|
|
|
|
}
|
2002-07-01 02:54:21 +02:00
|
|
|
|
2014-04-17 06:06:01 +02:00
|
|
|
return rc_flag;
|
2002-06-30 04:21:31 +02:00
|
|
|
}
|
|
|
|
|
|
2002-07-01 02:54:21 +02:00
|
|
|
bool NetEvWait::synth_async(Design*des, NetScope*scope,
|
2014-02-15 23:16:22 +01:00
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
|
|
|
|
NetBus&accumulated_nex_out)
|
2002-06-30 04:21:31 +02:00
|
|
|
{
|
2014-02-15 23:16:22 +01:00
|
|
|
bool flag = statement_->synth_async(des, scope, nex_map, nex_out, accumulated_nex_out);
|
2003-12-17 17:52:39 +01:00
|
|
|
return flag;
|
2002-06-30 04:21:31 +02:00
|
|
|
}
|
|
|
|
|
|
2014-05-02 05:37:33 +02:00
|
|
|
bool NetForLoop::synth_async(Design*des, NetScope*scope,
|
|
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
|
|
|
|
NetBus&accumulated_nex_out)
|
|
|
|
|
{
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetForLoop::synth_async: "
|
|
|
|
|
<< "Index variable is " << index_->name() << endl;
|
|
|
|
|
cerr << get_fileline() << ": NetForLoop::synth_async: "
|
|
|
|
|
<< "Initialization expression: " << *init_expr_ << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the step assignment statement and break it into the
|
|
|
|
|
// l-value (should be the index) and the r-value, which is the
|
|
|
|
|
// step expressions.
|
|
|
|
|
NetAssign*step_assign = dynamic_cast<NetAssign*> (step_statement_);
|
2014-05-03 03:30:44 +02:00
|
|
|
char assign_operator = step_assign->assign_operator();
|
2014-05-02 05:37:33 +02:00
|
|
|
ivl_assert(*this, step_assign);
|
|
|
|
|
NetExpr*step_expr = step_assign->rval();
|
|
|
|
|
|
|
|
|
|
// Tell the scope that this index value is like a genvar.
|
|
|
|
|
LocalVar index_var;
|
|
|
|
|
index_var.nwords = 0;
|
|
|
|
|
|
|
|
|
|
map<perm_string,LocalVar> index_args;
|
|
|
|
|
|
|
|
|
|
// Calculate the initial value for the index.
|
|
|
|
|
index_var.value = init_expr_->evaluate_function(*this, index_args);
|
|
|
|
|
ivl_assert(*this, index_var.value);
|
|
|
|
|
index_args[index_->name()] = index_var;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
// Evaluate the condition expression. If it is false,
|
|
|
|
|
// then we are going to break out of this synthesis loop.
|
|
|
|
|
NetExpr*tmp = condition_->evaluate_function(*this, index_args);
|
|
|
|
|
ivl_assert(*this, tmp);
|
|
|
|
|
|
|
|
|
|
long cond_value;
|
|
|
|
|
bool rc = eval_as_long(cond_value, tmp);
|
|
|
|
|
ivl_assert(*this, rc);
|
|
|
|
|
delete tmp;
|
|
|
|
|
if (!cond_value) break;
|
|
|
|
|
|
|
|
|
|
scope->genvar_tmp = index_->name();
|
|
|
|
|
rc = eval_as_long(scope->genvar_tmp_val, index_var.value);
|
|
|
|
|
ivl_assert(*this, rc);
|
|
|
|
|
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetForLoop::synth_async: "
|
|
|
|
|
<< "Synthesis iteration with " << index_->name()
|
|
|
|
|
<< "=" << *index_var.value << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Synthesize the iterated expression. Stash the loop
|
|
|
|
|
// index value so that the substatements can see this
|
|
|
|
|
// value and use it during its own synthesis.
|
|
|
|
|
ivl_assert(*this, scope->loop_index_tmp.empty());
|
|
|
|
|
scope->loop_index_tmp = index_args;
|
2014-05-18 01:58:36 +02:00
|
|
|
|
|
|
|
|
rc = synth_async_block_substatement_(des, scope, nex_map,
|
|
|
|
|
accumulated_nex_out,
|
|
|
|
|
statement_);
|
|
|
|
|
|
2014-05-02 05:37:33 +02:00
|
|
|
scope->loop_index_tmp.clear();
|
|
|
|
|
|
|
|
|
|
// Evaluate the step_expr to generate the next index value.
|
|
|
|
|
tmp = step_expr->evaluate_function(*this, index_args);
|
|
|
|
|
ivl_assert(*this, tmp);
|
2014-05-03 03:30:44 +02:00
|
|
|
|
|
|
|
|
// If there is an assign_operator, then replace the
|
|
|
|
|
// index_var.value with (value <op> tmp) and evaluate
|
|
|
|
|
// that to get the next value. "value" is the existing
|
|
|
|
|
// value, and "tmp" is the step value. We are replacing
|
|
|
|
|
// (value += tmp) with (value = value + tmp) and
|
|
|
|
|
// evaluating it.
|
|
|
|
|
switch (assign_operator) {
|
|
|
|
|
case 0:
|
|
|
|
|
break;
|
|
|
|
|
case '+':
|
|
|
|
|
case '-':
|
|
|
|
|
index_var.value = new NetEBAdd(assign_operator, tmp, index_var.value, 32, true);
|
|
|
|
|
tmp = index_var.value->evaluate_function(*this, index_args);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "NetForLoop::synth_async: What to do with assign_operator=" << assign_operator << endl;
|
|
|
|
|
ivl_assert(*this, 0);
|
|
|
|
|
}
|
2014-05-02 05:37:33 +02:00
|
|
|
delete index_var.value;
|
|
|
|
|
index_var.value = tmp;
|
|
|
|
|
index_args[index_->name()] = index_var;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete index_var.value;
|
2014-05-03 03:30:44 +02:00
|
|
|
|
2014-05-18 01:58:36 +02:00
|
|
|
// The output from the block is now the accumulated outputs.
|
|
|
|
|
for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1)
|
|
|
|
|
connect(nex_out.pin(idx), accumulated_nex_out.pin(idx));
|
|
|
|
|
|
2014-05-02 05:37:33 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-25 01:44:01 +02:00
|
|
|
/*
|
|
|
|
|
* This method is called when the process is shown to be
|
|
|
|
|
* asynchronous. Figure out the nexus set of outputs from this
|
|
|
|
|
* process, and pass that to the synth_async method for the statement
|
|
|
|
|
* of the process. The statement will connect its output to the
|
|
|
|
|
* nex_out set, using the nex_map as a guide. Starting from the top,
|
|
|
|
|
* the nex_map is the same as the nex_map.
|
|
|
|
|
*/
|
2002-06-30 04:21:31 +02:00
|
|
|
bool NetProcTop::synth_async(Design*des)
|
|
|
|
|
{
|
2002-07-01 02:54:21 +02:00
|
|
|
NexusSet nex_set;
|
|
|
|
|
statement_->nex_output(nex_set);
|
|
|
|
|
|
2005-04-25 01:44:01 +02:00
|
|
|
if (debug_synth2) {
|
2013-09-09 03:18:31 +02:00
|
|
|
cerr << get_fileline() << ": NetProcTop::synth_async: "
|
|
|
|
|
<< "Process has " << nex_set.size() << " outputs." << endl;
|
2005-04-25 01:44:01 +02:00
|
|
|
}
|
|
|
|
|
|
2013-09-09 03:18:31 +02:00
|
|
|
NetBus nex_q (scope(), nex_set.size());
|
|
|
|
|
for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) {
|
2014-02-15 23:16:22 +01:00
|
|
|
NexusSet::elem_t&item = nex_set[idx];
|
|
|
|
|
if (item.base != 0 || item.wid!=item.lnk.nexus()->vector_width()) {
|
2013-09-09 03:18:31 +02:00
|
|
|
ivl_variable_type_t tmp_data_type = IVL_VT_LOGIC;
|
2014-02-15 23:16:22 +01:00
|
|
|
netvector_t*tmp_type = new netvector_t(tmp_data_type, item.lnk.nexus()->vector_width()-1,0);
|
2013-09-09 03:18:31 +02:00
|
|
|
NetNet*tmp_sig = new NetNet(scope(), scope()->local_symbol(),
|
2014-07-14 03:09:13 +02:00
|
|
|
NetNet::WIRE, NetNet::not_an_array, tmp_type);
|
2013-09-09 03:18:31 +02:00
|
|
|
tmp_sig->local_flag(true);
|
2015-01-15 01:38:57 +01:00
|
|
|
tmp_sig->set_line(*this);
|
2013-09-09 03:18:31 +02:00
|
|
|
|
|
|
|
|
NetPartSelect*tmp = new NetPartSelect(tmp_sig, item.base,
|
|
|
|
|
item.wid, NetPartSelect::PV);
|
|
|
|
|
des->add_node(tmp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
connect(tmp->pin(0), nex_q.pin(idx));
|
2014-02-15 23:16:22 +01:00
|
|
|
connect(item.lnk, tmp_sig->pin(0));
|
2013-09-09 03:18:31 +02:00
|
|
|
|
|
|
|
|
} else {
|
2014-02-15 23:16:22 +01:00
|
|
|
connect(item.lnk, nex_q.pin(idx));
|
2013-09-09 03:18:31 +02:00
|
|
|
}
|
2005-04-25 03:35:58 +02:00
|
|
|
}
|
2002-07-01 02:54:21 +02:00
|
|
|
|
2014-02-15 23:16:22 +01:00
|
|
|
NetBus tmp_q (scope(), nex_set.size());
|
|
|
|
|
bool flag = statement_->synth_async(des, scope(), nex_set, nex_q, tmp_q);
|
2002-07-01 02:54:21 +02:00
|
|
|
return flag;
|
2002-06-30 04:21:31 +02:00
|
|
|
}
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
/*
|
|
|
|
|
* This method is called when a block is encountered near the surface
|
|
|
|
|
* of a synchronous always statement. For example, this code will be
|
|
|
|
|
* invoked for input like this:
|
|
|
|
|
*
|
|
|
|
|
* always @(posedge clk...) begin
|
|
|
|
|
* <statement1>
|
|
|
|
|
* <statement2>
|
|
|
|
|
* ...
|
|
|
|
|
* end
|
|
|
|
|
*
|
|
|
|
|
* This needs to be split into a DFF bank for each statement, because
|
|
|
|
|
* the statements may each infer different reset and enable signals.
|
|
|
|
|
*/
|
2013-08-25 04:39:16 +02:00
|
|
|
bool NetBlock::synth_sync(Design*des, NetScope*scope,
|
2015-06-13 16:56:12 +02:00
|
|
|
bool&ff_negedge,
|
2014-07-20 00:17:53 +02:00
|
|
|
NetNet*ff_clk, NetBus&ff_ce,
|
2014-06-16 04:10:09 +02:00
|
|
|
NetBus&ff_aclr,NetBus&ff_aset,
|
2015-06-13 16:56:12 +02:00
|
|
|
vector<verinum>&ff_aset_value,
|
2014-02-15 23:16:22 +01:00
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
2013-08-25 04:39:16 +02:00
|
|
|
const vector<NetEvProbe*>&events_in)
|
2002-10-21 03:42:08 +02:00
|
|
|
{
|
2014-04-20 17:24:28 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetBlock::synth_sync: "
|
|
|
|
|
<< "Examine this block for synchronous logic." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
bool flag = true;
|
|
|
|
|
|
|
|
|
|
NetProc*cur = last_;
|
|
|
|
|
do {
|
|
|
|
|
cur = cur->next_;
|
|
|
|
|
|
|
|
|
|
/* Create a temporary nex_map for the substatement. */
|
|
|
|
|
NexusSet tmp_set;
|
|
|
|
|
cur->nex_output(tmp_set);
|
|
|
|
|
|
|
|
|
|
/* Create also a temporary net_out to collect the
|
2002-11-09 21:22:57 +01:00
|
|
|
output. The tmp1 and tmp2 map and out sets together
|
|
|
|
|
are used to collect the outputs from the substatement
|
|
|
|
|
for the inputs of the FF bank. */
|
2013-09-09 03:18:31 +02:00
|
|
|
NetBus tmp_out (scope, tmp_set.size());
|
2016-01-30 23:38:08 +01:00
|
|
|
for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) {
|
|
|
|
|
unsigned ptr = nex_map.find_nexus(tmp_set[idx]);
|
|
|
|
|
ivl_assert(*this, ptr < nex_out.pin_count());
|
|
|
|
|
if (nex_out.pin(ptr).is_linked()) {
|
|
|
|
|
cerr << get_fileline() << ": sorry: multiple statements "
|
|
|
|
|
"assigning to the same flip-flop are not yet "
|
|
|
|
|
"supported in synthesis." << endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-10-23 03:45:24 +02:00
|
|
|
|
2014-07-20 00:17:53 +02:00
|
|
|
/* Create a temporary ff_ce (FF clock-enable) that
|
|
|
|
|
accounts for the subset of outputs that this
|
|
|
|
|
substatement drives. This allows for the possibility
|
|
|
|
|
that the substatement has CE patterns of its own. */
|
|
|
|
|
NetBus tmp_ce (scope, tmp_set.size());
|
|
|
|
|
for (unsigned idx = 0 ; idx < tmp_ce.pin_count() ; idx += 1) {
|
|
|
|
|
unsigned ptr = nex_map.find_nexus(tmp_set[idx]);
|
|
|
|
|
ivl_assert(*this, ptr < nex_out.pin_count());
|
|
|
|
|
if (ff_ce.pin(ptr).is_linked()) {
|
|
|
|
|
connect(tmp_ce.pin(idx), ff_ce.pin(ptr));
|
|
|
|
|
ff_ce.pin(ptr).unlink();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
/* Now go on with the synchronous synthesis for this
|
2003-08-14 04:41:05 +02:00
|
|
|
subset of the statement. The tmp_map is the output
|
|
|
|
|
nexa that we expect, and the tmp_out is where we want
|
|
|
|
|
those outputs connected. */
|
2015-06-13 16:56:12 +02:00
|
|
|
bool ok_flag = cur->synth_sync(des, scope,
|
|
|
|
|
ff_negedge, ff_clk, tmp_ce,
|
|
|
|
|
ff_aclr, ff_aset, ff_aset_value,
|
2013-09-09 03:18:31 +02:00
|
|
|
tmp_set, tmp_out, events_in);
|
2002-10-21 03:42:08 +02:00
|
|
|
flag = flag && ok_flag;
|
|
|
|
|
|
|
|
|
|
if (ok_flag == false)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Use the nex_map to link up the output from the
|
2002-11-06 04:22:28 +01:00
|
|
|
substatement to the output of the block as a
|
|
|
|
|
whole. It is occasionally possible to have outputs
|
|
|
|
|
beyond the input set, for example when the l-value of
|
2011-03-11 20:27:54 +01:00
|
|
|
an assignment is smaller than the r-value. */
|
2013-08-24 03:22:32 +02:00
|
|
|
for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) {
|
2013-09-09 03:18:31 +02:00
|
|
|
unsigned ptr = nex_map.find_nexus(tmp_set[idx]);
|
2014-07-20 00:17:53 +02:00
|
|
|
ivl_assert(*this, ptr < nex_out.pin_count());
|
2013-08-24 03:22:32 +02:00
|
|
|
connect(nex_out.pin(ptr), tmp_out.pin(idx));
|
2014-07-20 00:17:53 +02:00
|
|
|
connect(ff_ce.pin(ptr), tmp_ce.pin(idx));
|
2002-10-21 03:42:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} while (cur != last_);
|
|
|
|
|
|
2014-04-20 17:24:28 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetBlock::synth_sync: "
|
|
|
|
|
<< "Done Examining this block for synchronous logic." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This method handles the case where I find a conditional near the
|
|
|
|
|
* surface of a synchronous thread. This conditional can be a CE or an
|
|
|
|
|
* asynchronous set/reset, depending on whether the pin of the
|
|
|
|
|
* expression is connected to an event, or not.
|
|
|
|
|
*/
|
2013-08-25 04:39:16 +02:00
|
|
|
bool NetCondit::synth_sync(Design*des, NetScope*scope,
|
2015-06-13 16:56:12 +02:00
|
|
|
bool&ff_negedge,
|
2014-07-20 00:17:53 +02:00
|
|
|
NetNet*ff_clk, NetBus&ff_ce,
|
2014-06-16 04:10:09 +02:00
|
|
|
NetBus&ff_aclr,NetBus&ff_aset,
|
2015-06-13 16:56:12 +02:00
|
|
|
vector<verinum>&ff_aset_value,
|
2014-02-15 23:16:22 +01:00
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
2013-08-25 04:39:16 +02:00
|
|
|
const vector<NetEvProbe*>&events_in)
|
2002-09-16 02:30:33 +02:00
|
|
|
{
|
2003-08-10 19:04:23 +02:00
|
|
|
/* First try to turn the condition expression into an
|
|
|
|
|
asynchronous set/reset. If the condition expression has
|
|
|
|
|
inputs that are included in the sensitivity list, then it
|
|
|
|
|
is likely intended as an asynchronous input. */
|
2002-11-10 00:29:29 +01:00
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
NexusSet*expr_input = expr_->nex_input();
|
|
|
|
|
assert(expr_input);
|
2013-08-25 04:39:16 +02:00
|
|
|
for (unsigned idx = 0 ; idx < events_in.size() ; idx += 1) {
|
2002-09-26 03:13:14 +02:00
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
NetEvProbe*ev = events_in[idx];
|
|
|
|
|
NexusSet pin_set;
|
2013-09-09 03:18:31 +02:00
|
|
|
pin_set.add(ev->pin(0).nexus(), 0, 0);
|
2002-10-23 03:45:24 +02:00
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
if (! expr_input->contains(pin_set))
|
|
|
|
|
continue;
|
2002-10-23 03:45:24 +02:00
|
|
|
|
2014-06-16 03:22:02 +02:00
|
|
|
// Synthesize the set/reset input expression.
|
|
|
|
|
NetNet*rst = expr_->synthesize(des, scope, expr_);
|
|
|
|
|
ivl_assert(*this, rst->pin_count() == 1);
|
2003-08-10 19:04:23 +02:00
|
|
|
|
|
|
|
|
/* XXXX I really should find a way to check that the
|
|
|
|
|
edge used on the reset input is correct. This would
|
2008-01-25 22:34:51 +01:00
|
|
|
involve interpreting the expression that is fed by the
|
2003-08-10 19:04:23 +02:00
|
|
|
reset expression. */
|
2014-06-16 03:22:02 +02:00
|
|
|
ivl_assert(*this, ev->edge() == NetEvProbe::POSEDGE);
|
|
|
|
|
|
|
|
|
|
// Synthesize the true clause to figure out what kind of
|
|
|
|
|
// set/reset we have. This should synthesize down to a
|
|
|
|
|
// constant. If not, we have an asynchronous LOAD, a
|
|
|
|
|
// very different beast.
|
|
|
|
|
ivl_assert(*this, if_);
|
|
|
|
|
bool flag;
|
|
|
|
|
NetBus tmp_out(scope, nex_out.pin_count());
|
|
|
|
|
NetBus accumulated_tmp_out(scope, nex_out.pin_count());
|
|
|
|
|
flag = if_->synth_async(des, scope, nex_map, tmp_out, accumulated_tmp_out);
|
2014-08-08 01:24:32 +02:00
|
|
|
if (! flag) return false;
|
2014-06-16 03:22:02 +02:00
|
|
|
|
2014-06-16 04:10:09 +02:00
|
|
|
ivl_assert(*this, tmp_out.pin_count() == ff_aclr.pin_count());
|
|
|
|
|
ivl_assert(*this, tmp_out.pin_count() == ff_aset.pin_count());
|
2014-06-16 03:22:02 +02:00
|
|
|
|
2014-06-16 04:10:09 +02:00
|
|
|
for (unsigned pin = 0 ; pin < tmp_out.pin_count() ; pin += 1) {
|
|
|
|
|
Nexus*rst_nex = tmp_out.pin(pin).nexus();
|
2003-08-10 19:04:23 +02:00
|
|
|
|
2015-06-13 16:56:12 +02:00
|
|
|
if (! rst_nex->drivers_constant()) {
|
|
|
|
|
cerr << get_fileline() << ": sorry: "
|
|
|
|
|
<< "Asynchronous LOAD not implemented." << endl;
|
|
|
|
|
return false;
|
2014-06-16 04:10:09 +02:00
|
|
|
}
|
2003-08-10 19:04:23 +02:00
|
|
|
|
2014-06-16 04:10:09 +02:00
|
|
|
verinum rst_drv = rst_nex->driven_vector();
|
2003-08-10 19:04:23 +02:00
|
|
|
|
2014-06-16 04:10:09 +02:00
|
|
|
verinum zero (verinum::V0, rst_drv.len());
|
|
|
|
|
verinum ones (verinum::V1, rst_drv.len());
|
2003-08-10 19:04:23 +02:00
|
|
|
|
2015-04-23 03:20:20 +02:00
|
|
|
if (rst_drv==zero) {
|
2015-06-08 22:20:49 +02:00
|
|
|
// Don't yet support multiple asynchronous reset inputs.
|
2015-04-23 03:20:20 +02:00
|
|
|
ivl_assert(*this, ! ff_aclr.pin(pin).is_linked());
|
2015-04-22 08:00:27 +02:00
|
|
|
|
2015-04-23 03:20:20 +02:00
|
|
|
ivl_assert(*this, rst->pin_count()==1);
|
|
|
|
|
connect(ff_aclr.pin(pin), rst->pin(0));
|
2015-04-22 08:00:27 +02:00
|
|
|
|
2015-04-23 03:20:20 +02:00
|
|
|
} else {
|
2015-06-08 22:20:49 +02:00
|
|
|
// Don't yet support multiple asynchronous set inputs.
|
2015-04-23 03:20:20 +02:00
|
|
|
ivl_assert(*this, ! ff_aset.pin(pin).is_linked());
|
2015-06-08 22:20:49 +02:00
|
|
|
|
2015-04-23 03:20:20 +02:00
|
|
|
ivl_assert(*this, rst->pin_count()==1);
|
|
|
|
|
connect(ff_aset.pin(pin), rst->pin(0));
|
2015-06-08 22:20:49 +02:00
|
|
|
if (rst_drv!=ones)
|
2015-06-13 16:56:12 +02:00
|
|
|
ff_aset_value[pin] = rst_drv;
|
2015-04-22 08:00:27 +02:00
|
|
|
}
|
2002-09-26 03:13:14 +02:00
|
|
|
}
|
|
|
|
|
|
2015-06-13 16:56:12 +02:00
|
|
|
return else_->synth_sync(des, scope,
|
|
|
|
|
ff_negedge, ff_clk, ff_ce,
|
|
|
|
|
ff_aclr, ff_aset, ff_aset_value,
|
2014-06-16 03:22:02 +02:00
|
|
|
nex_map, nex_out, vector<NetEvProbe*>(0));
|
2002-09-26 03:13:14 +02:00
|
|
|
}
|
|
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
delete expr_input;
|
2013-08-25 04:39:16 +02:00
|
|
|
#if 0
|
2003-08-15 04:23:52 +02:00
|
|
|
/* Detect the case that this is a *synchronous* set/reset. It
|
2008-06-12 19:04:29 +02:00
|
|
|
is not asynchronous because we know the condition is not
|
2003-08-15 04:23:52 +02:00
|
|
|
included in the sensitivity list, but if the if_ case is
|
|
|
|
|
constant (has no inputs) then we can model this as a
|
2003-10-27 03:18:04 +01:00
|
|
|
synchronous set/reset.
|
|
|
|
|
|
|
|
|
|
This is only synchronous set/reset if there is a true and a
|
|
|
|
|
false clause, and no inputs. The "no inputs" requirement is
|
|
|
|
|
met if the assignments are of all constant values. */
|
|
|
|
|
assert(if_ != 0);
|
2003-08-15 04:23:52 +02:00
|
|
|
NexusSet*a_set = if_->nex_input();
|
|
|
|
|
|
2003-10-27 03:18:04 +01:00
|
|
|
if ((a_set->count() == 0) && if_ && else_) {
|
2003-08-15 04:23:52 +02:00
|
|
|
|
|
|
|
|
NetNet*rst = expr_->synthesize(des);
|
|
|
|
|
assert(rst->pin_count() == 1);
|
|
|
|
|
|
|
|
|
|
/* Synthesize the true clause to figure out what
|
|
|
|
|
kind of set/reset we have. */
|
|
|
|
|
NetNet*asig = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, nex_map->pin_count());
|
|
|
|
|
asig->local_flag(true);
|
|
|
|
|
bool flag = if_->synth_async(des, scope, nex_map, asig);
|
|
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
if (!flag) {
|
|
|
|
|
/* This path leads nowhere */
|
|
|
|
|
delete asig;
|
|
|
|
|
} else {
|
|
|
|
|
assert(asig->pin_count() == ff->width());
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
/* Collect the set/reset value into a verinum. If
|
|
|
|
|
this turns out to be entirely 0 values, then
|
|
|
|
|
use the Sclr input. Otherwise, use the Aset
|
|
|
|
|
input and save the set value. */
|
|
|
|
|
verinum tmp (verinum::V0, ff->width());
|
|
|
|
|
for (unsigned bit = 0 ; bit < ff->width() ; bit += 1) {
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
assert(asig->pin(bit).nexus()->drivers_constant());
|
|
|
|
|
tmp.set(bit, asig->pin(bit).nexus()->driven_value());
|
|
|
|
|
}
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
assert(tmp.is_defined());
|
|
|
|
|
if (tmp.is_zero()) {
|
|
|
|
|
connect(ff->pin_Sclr(), rst->pin(0));
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
} else {
|
|
|
|
|
connect(ff->pin_Sset(), rst->pin(0));
|
|
|
|
|
ff->sset_value(tmp);
|
|
|
|
|
}
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
delete a_set;
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
assert(else_ != 0);
|
|
|
|
|
flag = else_->synth_sync(des, scope, ff, nex_map,
|
|
|
|
|
nex_out, svector<NetEvProbe*>(0))
|
|
|
|
|
&& flag;
|
|
|
|
|
DEBUG_SYNTH2_EXIT("NetCondit",flag)
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
2003-08-15 04:23:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete a_set;
|
2013-08-25 04:39:16 +02:00
|
|
|
#endif
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2002-09-26 03:13:14 +02:00
|
|
|
/* Failed to find an asynchronous set/reset, so any events
|
|
|
|
|
input are probably in error. */
|
2013-08-25 04:39:16 +02:00
|
|
|
if (events_in.size() > 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: Events are unaccounted"
|
2002-09-26 03:13:14 +02:00
|
|
|
<< " for in process synthesis." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
/* If this is an if/then/else, then it is likely a
|
|
|
|
|
combinational if, and I should synthesize it that way. */
|
|
|
|
|
if (if_ && else_) {
|
2014-02-15 23:16:22 +01:00
|
|
|
NetBus tmp (scope, nex_out.pin_count());
|
|
|
|
|
bool flag = synth_async(des, scope, nex_map, nex_out, tmp);
|
2003-12-17 17:52:39 +01:00
|
|
|
return flag;
|
2002-09-16 02:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2013-08-25 04:39:16 +02:00
|
|
|
ivl_assert(*this, if_);
|
|
|
|
|
ivl_assert(*this, !else_);
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2003-08-14 04:41:05 +02:00
|
|
|
/* Synthesize the enable expression. */
|
2013-08-25 04:39:16 +02:00
|
|
|
NetNet*ce = expr_->synthesize(des, scope, expr_);
|
2014-07-20 00:17:53 +02:00
|
|
|
ivl_assert(*this, ce && ce->pin_count()==1 && ce->vector_width()==1);
|
|
|
|
|
|
|
|
|
|
if (debug_synth2) {
|
|
|
|
|
NexusSet if_set;
|
|
|
|
|
if_->nex_output(if_set);
|
|
|
|
|
|
|
|
|
|
cerr << get_fileline() << ": NetCondit::synth_sync: "
|
|
|
|
|
<< "Found ce pattern."
|
|
|
|
|
<< " ff_ce.pin_count()=" << ff_ce.pin_count()
|
|
|
|
|
<< endl;
|
|
|
|
|
for (unsigned idx = 0 ; idx < nex_map.size() ; idx += 1) {
|
|
|
|
|
cerr << get_fileline() << ": NetCondit::synth_sync: "
|
|
|
|
|
<< "nex_map[" << idx << "]: "
|
|
|
|
|
<< "base=" << nex_map[idx].base
|
|
|
|
|
<< ", wid=" << nex_map[idx].wid
|
|
|
|
|
<< endl;
|
|
|
|
|
nex_map[idx].lnk.dump_link(cerr, 8);
|
|
|
|
|
}
|
|
|
|
|
for (unsigned idx = 0 ; idx < if_set.size() ; idx += 1) {
|
|
|
|
|
cerr << get_fileline() << ": NetCondit::synth_sync: "
|
|
|
|
|
<< "if_set[" << idx << "]: "
|
|
|
|
|
<< "base=" << if_set[idx].base
|
|
|
|
|
<< ", wid=" << if_set[idx].wid
|
|
|
|
|
<< endl;
|
|
|
|
|
if_set[idx].lnk.dump_link(cerr, 8);
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-08-14 04:41:05 +02:00
|
|
|
|
2002-11-10 00:29:29 +01:00
|
|
|
/* What's left, is a synchronous CE statement like this:
|
|
|
|
|
|
|
|
|
|
if (expr_) <true statement>;
|
|
|
|
|
|
2003-01-27 06:09:17 +01:00
|
|
|
The expr_ expression has already been synthesized to the ce
|
2002-11-10 00:29:29 +01:00
|
|
|
net, so we connect it here to the FF. What's left is to
|
|
|
|
|
synthesize the substatement as a combinational
|
|
|
|
|
statement.
|
|
|
|
|
|
|
|
|
|
Watch out for the special case that there is already a CE
|
|
|
|
|
connected to this FF. This can be caused by code like this:
|
|
|
|
|
|
|
|
|
|
if (a) if (b) <statement>;
|
|
|
|
|
|
|
|
|
|
In this case, we are working on the inner IF, so we AND the
|
|
|
|
|
a and b expressions to make a new CE. */
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2014-07-20 00:17:53 +02:00
|
|
|
for (unsigned idx = 0 ; idx < ff_ce.pin_count() ; idx += 1) {
|
|
|
|
|
if (ff_ce.pin(idx).is_linked()) {
|
|
|
|
|
NetLogic*ce_and = new NetLogic(scope,
|
|
|
|
|
scope->local_symbol(), 3,
|
|
|
|
|
NetLogic::AND, 1);
|
|
|
|
|
des->add_node(ce_and);
|
|
|
|
|
connect(ff_ce.pin(idx), ce_and->pin(1));
|
|
|
|
|
connect(ce->pin(0), ce_and->pin(2));
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2014-07-20 00:17:53 +02:00
|
|
|
ff_ce.pin(idx).unlink();
|
|
|
|
|
connect(ff_ce.pin(idx), ce_and->pin(0));
|
2002-11-10 00:29:29 +01:00
|
|
|
|
2014-07-20 00:17:53 +02:00
|
|
|
} else {
|
2002-11-10 00:29:29 +01:00
|
|
|
|
2014-07-20 00:17:53 +02:00
|
|
|
connect(ff_ce.pin(idx), ce->pin(0));
|
|
|
|
|
}
|
2002-11-10 00:29:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-06-13 16:56:12 +02:00
|
|
|
bool flag = if_->synth_sync(des, scope,
|
|
|
|
|
ff_negedge, ff_clk, ff_ce,
|
|
|
|
|
ff_aclr, ff_aset, ff_aset_value,
|
|
|
|
|
nex_map, nex_out, events_in);
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2003-12-17 17:52:39 +01:00
|
|
|
return flag;
|
2002-09-16 02:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2013-08-25 04:39:16 +02:00
|
|
|
bool NetEvWait::synth_sync(Design*des, NetScope*scope,
|
2015-06-13 16:56:12 +02:00
|
|
|
bool&ff_negedge,
|
2014-07-20 00:17:53 +02:00
|
|
|
NetNet*ff_clk, NetBus&ff_ce,
|
2014-06-16 04:10:09 +02:00
|
|
|
NetBus&ff_aclr,NetBus&ff_aset,
|
2015-06-13 16:56:12 +02:00
|
|
|
vector<verinum>&ff_aset_value,
|
2014-02-15 23:16:22 +01:00
|
|
|
NexusSet&nex_map, NetBus&nex_out,
|
2013-08-23 05:00:23 +02:00
|
|
|
const vector<NetEvProbe*>&events_in)
|
2002-09-16 02:30:33 +02:00
|
|
|
{
|
2014-04-20 17:24:28 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetEvWait::synth_sync: "
|
|
|
|
|
<< "Synchronous process an event statement." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-23 05:00:23 +02:00
|
|
|
if (events_in.size() > 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Events are unaccounted"
|
2002-09-26 03:13:14 +02:00
|
|
|
<< " for in process synthesis." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-23 05:00:23 +02:00
|
|
|
assert(events_in.size() == 0);
|
2002-09-26 03:13:14 +02:00
|
|
|
|
2007-03-22 17:08:14 +01:00
|
|
|
/* This can't be other than one unless there are named events,
|
2002-09-24 02:58:35 +02:00
|
|
|
which I cannot synthesize. */
|
2014-05-08 23:00:39 +02:00
|
|
|
ivl_assert(*this, events_.size() == 1);
|
2002-09-16 02:30:33 +02:00
|
|
|
NetEvent*ev = events_[0];
|
|
|
|
|
|
2002-09-24 02:58:35 +02:00
|
|
|
assert(ev->nprobe() >= 1);
|
2013-08-23 05:00:23 +02:00
|
|
|
vector<NetEvProbe*>events (ev->nprobe() - 1);
|
2002-09-24 02:58:35 +02:00
|
|
|
|
|
|
|
|
/* Get the input set from the substatement. This will be used
|
2005-04-25 01:44:01 +02:00
|
|
|
to figure out which of the probes is the clock. */
|
2002-09-24 02:58:35 +02:00
|
|
|
NexusSet*statement_input = statement_ -> nex_input();
|
|
|
|
|
|
|
|
|
|
/* Search for a clock input. The clock input is the edge event
|
|
|
|
|
that is not also an input to the substatement. */
|
|
|
|
|
NetEvProbe*pclk = 0;
|
2002-09-26 03:13:14 +02:00
|
|
|
unsigned event_idx = 0;
|
2002-09-24 02:58:35 +02:00
|
|
|
for (unsigned idx = 0 ; idx < ev->nprobe() ; idx += 1) {
|
|
|
|
|
NetEvProbe*tmp = ev->probe(idx);
|
|
|
|
|
assert(tmp->pin_count() == 1);
|
|
|
|
|
|
|
|
|
|
NexusSet tmp_nex;
|
2013-09-09 03:18:31 +02:00
|
|
|
tmp_nex .add( tmp->pin(0).nexus(), 0, 0 );
|
2002-09-24 02:58:35 +02:00
|
|
|
|
|
|
|
|
if (! statement_input ->contains(tmp_nex)) {
|
|
|
|
|
if (pclk != 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Too many "
|
2002-09-24 02:58:35 +02:00
|
|
|
<< "clocks for synchronous logic." << endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : Perhaps an"
|
2002-09-24 02:58:35 +02:00
|
|
|
<< " asynchronous set/reset is misused?" << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
pclk = tmp;
|
2002-09-26 03:13:14 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
events[event_idx++] = tmp;
|
2002-09-24 02:58:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2002-09-24 02:58:35 +02:00
|
|
|
if (pclk == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: None of the edges"
|
2002-09-24 02:58:35 +02:00
|
|
|
<< " are valid clock inputs." << endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : Perhaps the clock"
|
2002-09-24 02:58:35 +02:00
|
|
|
<< " is read by a statement or expression?" << endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2014-04-20 17:24:28 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": NetEvWait::synth_sync: "
|
|
|
|
|
<< "Found and synthesized the FF clock." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-25 04:39:16 +02:00
|
|
|
connect(ff_clk->pin(0), pclk->pin(0));
|
2005-04-25 01:44:01 +02:00
|
|
|
if (pclk->edge() == NetEvProbe::NEGEDGE) {
|
2015-06-13 16:56:12 +02:00
|
|
|
ff_negedge = true;
|
2005-04-25 01:44:01 +02:00
|
|
|
|
|
|
|
|
if (debug_synth2) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: "
|
2005-04-25 01:44:01 +02:00
|
|
|
<< "Detected a NEGEDGE clock for the synthesized ff."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2002-09-24 02:58:35 +02:00
|
|
|
/* Synthesize the input to the DFF. */
|
2015-06-13 16:56:12 +02:00
|
|
|
bool flag = statement_->synth_sync(des, scope,
|
|
|
|
|
ff_negedge, ff_clk, ff_ce,
|
|
|
|
|
ff_aclr, ff_aset, ff_aset_value,
|
2002-09-26 03:13:14 +02:00
|
|
|
nex_map, nex_out, events);
|
2002-09-24 02:58:35 +02:00
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-25 01:44:01 +02:00
|
|
|
/*
|
|
|
|
|
* This method is called for a process that is determined to be
|
|
|
|
|
* synchronous. Create a NetFF device to hold the output from the
|
|
|
|
|
* statement, and synthesize that statement in place.
|
|
|
|
|
*/
|
2002-09-16 02:30:33 +02:00
|
|
|
bool NetProcTop::synth_sync(Design*des)
|
|
|
|
|
{
|
2005-04-25 01:44:01 +02:00
|
|
|
if (debug_synth2) {
|
2014-04-20 17:24:28 +02:00
|
|
|
cerr << get_fileline() << ": NetProcTop::synth_sync: "
|
2005-04-25 01:44:01 +02:00
|
|
|
<< "Process is apparently synchronous. Making NetFFs."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
NexusSet nex_set;
|
|
|
|
|
statement_->nex_output(nex_set);
|
2015-04-23 03:20:20 +02:00
|
|
|
vector<verinum> aset_value(nex_set.size());
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2005-04-25 01:44:01 +02:00
|
|
|
/* Make a model FF that will connect to the first item in the
|
|
|
|
|
set, and will also take the initial connection of clocks
|
|
|
|
|
and resets. */
|
2005-05-15 06:45:50 +02:00
|
|
|
|
2013-08-24 03:22:32 +02:00
|
|
|
// Create a net to carry the clock for the synthesized FFs.
|
|
|
|
|
NetNet*clock = new NetNet(scope(), scope()->local_symbol(),
|
|
|
|
|
NetNet::TRI, &netvector_t::scalar_logic);
|
|
|
|
|
clock->local_flag(true);
|
2015-01-15 01:38:57 +01:00
|
|
|
clock->set_line(*this);
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2014-07-20 00:17:53 +02:00
|
|
|
#if 0
|
2013-08-25 04:39:16 +02:00
|
|
|
NetNet*ce = new NetNet(scope(), scope()->local_symbol(),
|
|
|
|
|
NetNet::TRI, &netvector_t::scalar_logic);
|
|
|
|
|
ce->local_flag(true);
|
2014-07-20 00:17:53 +02:00
|
|
|
#else
|
|
|
|
|
NetBus ce (scope(), nex_set.size());
|
|
|
|
|
#endif
|
2013-09-09 03:18:31 +02:00
|
|
|
NetBus nex_d (scope(), nex_set.size());
|
|
|
|
|
NetBus nex_q (scope(), nex_set.size());
|
2014-06-16 04:10:09 +02:00
|
|
|
NetBus aclr (scope(), nex_set.size());
|
|
|
|
|
NetBus aset (scope(), nex_set.size());
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2005-04-25 01:44:01 +02:00
|
|
|
/* The Q of the NetFF devices is connected to the output that
|
|
|
|
|
we are. The nex_q is a bundle of the outputs. We will also
|
|
|
|
|
pass the nex_q as a map to the statement's synth_sync
|
|
|
|
|
method to map it to the correct nex_d pin. */
|
2013-09-09 03:18:31 +02:00
|
|
|
for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) {
|
2014-02-15 23:16:22 +01:00
|
|
|
connect(nex_set[idx].lnk, nex_q.pin(idx));
|
2002-09-16 02:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2005-04-25 01:44:01 +02:00
|
|
|
// Connect the input later.
|
|
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
/* Synthesize the input to the DFF. */
|
2015-06-13 16:56:12 +02:00
|
|
|
bool negedge = false;
|
|
|
|
|
bool flag = statement_->synth_sync(des, scope(),
|
|
|
|
|
negedge, clock, ce,
|
|
|
|
|
aclr, aset, aset_value,
|
2013-09-09 03:18:31 +02:00
|
|
|
nex_set, nex_d,
|
2013-08-23 05:00:23 +02:00
|
|
|
vector<NetEvProbe*>());
|
2005-04-25 01:44:01 +02:00
|
|
|
if (! flag) {
|
2013-08-24 03:22:32 +02:00
|
|
|
delete clock;
|
2005-04-25 01:44:01 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2013-09-09 03:18:31 +02:00
|
|
|
for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) {
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2014-02-15 23:16:22 +01:00
|
|
|
//ivl_assert(*this, nex_set[idx].nex);
|
2013-08-24 03:22:32 +02:00
|
|
|
if (debug_synth2) {
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Top level making a "
|
2014-02-10 02:30:30 +01:00
|
|
|
<< nex_set[idx].wid << "-wide "
|
2013-08-24 03:22:32 +02:00
|
|
|
<< "NetFF device." << endl;
|
|
|
|
|
}
|
2005-04-25 01:44:01 +02:00
|
|
|
|
|
|
|
|
NetFF*ff2 = new NetFF(scope(), scope()->local_symbol(),
|
2015-06-13 16:56:12 +02:00
|
|
|
negedge, nex_set[idx].wid);
|
2005-04-25 01:44:01 +02:00
|
|
|
des->add_node(ff2);
|
2013-08-25 04:39:16 +02:00
|
|
|
ff2->set_line(*this);
|
2015-04-23 03:20:20 +02:00
|
|
|
ff2->aset_value(aset_value[idx]);
|
2005-04-25 01:44:01 +02:00
|
|
|
|
2013-08-24 03:22:32 +02:00
|
|
|
NetNet*tmp = nex_d.pin(idx).nexus()->pick_any_net();
|
2013-08-25 04:39:16 +02:00
|
|
|
tmp->set_line(*this);
|
2005-04-25 01:44:01 +02:00
|
|
|
assert(tmp);
|
|
|
|
|
|
|
|
|
|
tmp = crop_to_width(des, tmp, ff2->width());
|
|
|
|
|
|
|
|
|
|
connect(nex_q.pin(idx), ff2->pin_Q());
|
|
|
|
|
connect(tmp->pin(0), ff2->pin_Data());
|
|
|
|
|
|
2013-08-25 04:39:16 +02:00
|
|
|
connect(clock->pin(0), ff2->pin_Clock());
|
2014-07-20 00:17:53 +02:00
|
|
|
if (ce.pin(idx).is_linked())
|
|
|
|
|
connect(ce.pin(idx), ff2->pin_Enable());
|
2014-06-16 04:10:09 +02:00
|
|
|
if (aclr.pin(idx).is_linked())
|
|
|
|
|
connect(aclr.pin(idx), ff2->pin_Aclr());
|
|
|
|
|
if (aset.pin(idx).is_linked())
|
|
|
|
|
connect(aset.pin(idx), ff2->pin_Aset());
|
2013-08-24 03:22:32 +02:00
|
|
|
#if 0
|
2005-04-25 01:44:01 +02:00
|
|
|
if (ff->pin_Sset().is_linked())
|
|
|
|
|
connect(ff->pin_Sset(), ff2->pin_Sset());
|
|
|
|
|
if (ff->pin_Sclr().is_linked())
|
|
|
|
|
connect(ff->pin_Sclr(), ff2->pin_Sclr());
|
2013-08-24 03:22:32 +02:00
|
|
|
#endif
|
2005-04-25 01:44:01 +02:00
|
|
|
}
|
|
|
|
|
|
2013-08-25 04:39:16 +02:00
|
|
|
// The "clock" and "ce" nets were just to carry the connection
|
|
|
|
|
// back to the flip-flop. Delete them now. The connections
|
|
|
|
|
// will persist.
|
|
|
|
|
delete clock;
|
2014-07-20 00:17:53 +02:00
|
|
|
#if 0
|
2013-08-25 04:39:16 +02:00
|
|
|
delete ce;
|
2014-07-20 00:17:53 +02:00
|
|
|
#endif
|
2005-04-25 01:44:01 +02:00
|
|
|
return true;
|
2002-09-16 02:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2002-06-30 04:21:31 +02:00
|
|
|
class synth2_f : public functor_t {
|
|
|
|
|
|
|
|
|
|
public:
|
2012-02-12 22:21:30 +01:00
|
|
|
void process(Design*, NetProcTop*);
|
2002-06-30 04:21:31 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2002-07-01 02:54:21 +02:00
|
|
|
* Look at a process. If it is asynchronous, then synthesize it as an
|
|
|
|
|
* asynchronous process and delete the process itself for its gates.
|
2002-06-30 04:21:31 +02:00
|
|
|
*/
|
2012-02-12 22:21:30 +01:00
|
|
|
void synth2_f::process(Design*des, NetProcTop*top)
|
2002-06-30 04:21:31 +02:00
|
|
|
{
|
2004-02-20 19:53:33 +01:00
|
|
|
if (top->attribute(perm_string::literal("ivl_synthesis_off")).as_ulong() != 0)
|
2002-08-11 00:07:08 +02:00
|
|
|
return;
|
|
|
|
|
|
2003-06-23 02:14:44 +02:00
|
|
|
/* If the scope that contains this process as a cell attribute
|
|
|
|
|
attached to it, then skip synthesis. */
|
2004-02-20 19:53:33 +01:00
|
|
|
if (top->scope()->attribute(perm_string::literal("ivl_synthesis_cell")).len() > 0)
|
2003-06-23 02:14:44 +02:00
|
|
|
return;
|
|
|
|
|
|
2014-04-20 17:24:28 +02:00
|
|
|
if (top->is_synchronous()) {
|
2002-09-16 02:30:33 +02:00
|
|
|
bool flag = top->synth_sync(des);
|
2002-09-24 02:58:35 +02:00
|
|
|
if (! flag) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << top->get_fileline() << ": error: "
|
2002-10-20 21:19:37 +02:00
|
|
|
<< "Unable to synthesize synchronous process." << endl;
|
2002-09-24 02:58:35 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2002-09-16 02:30:33 +02:00
|
|
|
des->delete_process(top);
|
|
|
|
|
return;
|
2014-04-20 17:24:28 +02:00
|
|
|
}
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2002-07-01 02:54:21 +02:00
|
|
|
if (! top->is_asynchronous()) {
|
2002-08-19 00:07:16 +02:00
|
|
|
bool synth_error_flag = false;
|
2004-02-20 19:53:33 +01:00
|
|
|
if (top->attribute(perm_string::literal("ivl_combinational")).as_ulong() != 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << top->get_fileline() << ": error: "
|
2002-08-11 00:07:08 +02:00
|
|
|
<< "Process is marked combinational,"
|
2002-07-01 02:54:21 +02:00
|
|
|
<< " but isn't really." << endl;
|
2002-08-11 00:07:08 +02:00
|
|
|
des->errors += 1;
|
2002-08-19 00:07:16 +02:00
|
|
|
synth_error_flag = true;
|
2002-08-11 00:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2004-02-20 19:53:33 +01:00
|
|
|
if (top->attribute(perm_string::literal("ivl_synthesis_on")).as_ulong() != 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << top->get_fileline() << ": error: "
|
2002-08-11 00:07:08 +02:00
|
|
|
<< "Process is marked for synthesis,"
|
|
|
|
|
<< " but I can't do it." << endl;
|
|
|
|
|
des->errors += 1;
|
2002-08-19 00:07:16 +02:00
|
|
|
synth_error_flag = true;
|
2002-08-11 00:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2002-08-19 00:07:16 +02:00
|
|
|
if (! synth_error_flag)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << top->get_fileline() << ": warning: "
|
2002-08-19 00:07:16 +02:00
|
|
|
<< "Process not synthesized." << endl;
|
|
|
|
|
|
2002-06-30 04:21:31 +02:00
|
|
|
return;
|
2002-07-01 02:54:21 +02:00
|
|
|
}
|
2002-06-30 04:21:31 +02:00
|
|
|
|
2002-07-01 02:54:21 +02:00
|
|
|
if (! top->synth_async(des)) {
|
2014-11-09 21:11:03 +01:00
|
|
|
cerr << top->get_fileline() << ": error: "
|
|
|
|
|
<< "failed to synthesize asynchronous "
|
|
|
|
|
<< "logic for this process." << endl;
|
2007-11-17 03:10:16 +01:00
|
|
|
des->errors += 1;
|
2002-06-30 04:21:31 +02:00
|
|
|
return;
|
2002-07-01 02:54:21 +02:00
|
|
|
}
|
2002-06-30 04:21:31 +02:00
|
|
|
|
|
|
|
|
des->delete_process(top);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void synth2(Design*des)
|
|
|
|
|
{
|
|
|
|
|
synth2_f synth_obj;
|
|
|
|
|
des->functor(&synth_obj);
|
|
|
|
|
}
|