1998-11-04 00:28:49 +01:00
|
|
|
/*
|
2015-04-13 15:39:10 +02:00
|
|
|
* Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com)
|
2013-04-09 03:35:37 +02:00
|
|
|
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
|
1998-11-04 00:28:49 +01: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.
|
1998-11-04 00:28:49 +01:00
|
|
|
*/
|
|
|
|
|
|
2001-07-25 05:10:48 +02:00
|
|
|
# include "config.h"
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
/*
|
|
|
|
|
* Elaboration takes as input a complete parse tree and the name of a
|
|
|
|
|
* root module, and generates as output the elaborated design. This
|
|
|
|
|
* elaborated design is presented as a Module, which does not
|
|
|
|
|
* reference any other modules. It is entirely self contained.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
# include <typeinfo>
|
2008-01-05 00:23:47 +01:00
|
|
|
# include <cstdlib>
|
2003-01-14 22:16:18 +01:00
|
|
|
# include <sstream>
|
2001-10-19 23:53:24 +02:00
|
|
|
# include <list>
|
1998-11-04 00:28:49 +01:00
|
|
|
# include "pform.h"
|
2013-03-15 04:08:32 +01:00
|
|
|
# include "PClass.h"
|
2000-04-04 05:20:15 +02:00
|
|
|
# include "PEvent.h"
|
2006-04-10 02:37:42 +02:00
|
|
|
# include "PGenerate.h"
|
2013-02-17 23:42:07 +01:00
|
|
|
# include "PPackage.h"
|
2006-09-23 06:57:19 +02:00
|
|
|
# include "PSpec.h"
|
1998-11-04 00:28:49 +01:00
|
|
|
# include "netlist.h"
|
2014-11-04 23:55:40 +01:00
|
|
|
# include "netenum.h"
|
2012-09-15 19:27:43 +02:00
|
|
|
# include "netvector.h"
|
2012-11-22 20:08:13 +01:00
|
|
|
# include "netdarray.h"
|
2014-09-02 18:23:54 +02:00
|
|
|
# include "netparray.h"
|
2012-11-22 20:08:13 +01:00
|
|
|
# include "netclass.h"
|
1999-09-29 02:42:50 +02:00
|
|
|
# include "netmisc.h"
|
2000-04-28 18:50:53 +02:00
|
|
|
# include "util.h"
|
2001-10-21 01:02:39 +02:00
|
|
|
# include "parse_api.h"
|
2002-04-22 02:53:39 +02:00
|
|
|
# include "compiler.h"
|
2007-02-12 02:52:21 +01:00
|
|
|
# include "ivl_assert.h"
|
1998-11-04 00:28:49 +01:00
|
|
|
|
2002-05-23 05:08:50 +02:00
|
|
|
|
2010-11-01 22:37:06 +01:00
|
|
|
void PGate::elaborate(Design*, NetScope*) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-10-07 07:25:33 +02:00
|
|
|
cerr << "internal error: what kind of gate? " <<
|
|
|
|
|
typeid(*this).name() << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
1999-09-15 06:17:52 +02:00
|
|
|
/*
|
|
|
|
|
* Elaborate the continuous assign. (This is *not* the procedural
|
|
|
|
|
* assign.) Elaborate the lvalue and rvalue, and do the assignment.
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
void PGAssign::elaborate(Design*des, NetScope*scope) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2000-10-07 21:45:42 +02:00
|
|
|
assert(scope);
|
|
|
|
|
|
2006-01-02 06:33:19 +01:00
|
|
|
NetExpr* rise_time, *fall_time, *decay_time;
|
2006-01-03 06:22:14 +01:00
|
|
|
eval_delays(des, scope, rise_time, fall_time, decay_time, true);
|
1999-08-01 23:18:55 +02:00
|
|
|
|
2010-03-16 23:16:53 +01:00
|
|
|
ivl_drive_t drive0 = strength0();
|
|
|
|
|
ivl_drive_t drive1 = strength1();
|
2000-05-07 06:37:55 +02:00
|
|
|
|
1999-06-10 06:03:52 +02:00
|
|
|
assert(pin(0));
|
|
|
|
|
assert(pin(1));
|
1999-05-20 06:31:45 +02:00
|
|
|
|
1999-09-15 06:17:52 +02:00
|
|
|
/* Elaborate the l-value. */
|
2008-03-19 04:50:40 +01:00
|
|
|
NetNet*lval = pin(0)->elaborate_lnet(des, scope);
|
1999-05-20 06:31:45 +02:00
|
|
|
if (lval == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-17 01:08:38 +01:00
|
|
|
// If this turns out to be an assignment to an unpacked array,
|
|
|
|
|
// then handle that special case elsewhere.
|
|
|
|
|
if (lval->pin_count() > 1) {
|
|
|
|
|
elaborate_unpacked_array_(des, scope, lval);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-27 06:33:24 +02:00
|
|
|
ivl_assert(*this, lval->pin_count() == 1);
|
2004-12-11 03:31:25 +01:00
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
2014-03-17 01:08:38 +01:00
|
|
|
cerr << get_fileline() << ": PGAssign::elaborate: elaborated l-value"
|
|
|
|
|
<< " width=" << lval->vector_width()
|
|
|
|
|
<< ", pin_count=" << lval->pin_count() << endl;
|
2004-12-11 03:31:25 +01:00
|
|
|
}
|
1999-09-15 06:17:52 +02:00
|
|
|
|
2013-09-29 23:48:42 +02:00
|
|
|
NetExpr*rval_expr = elaborate_rval_expr(des, scope, lval->net_type(),
|
|
|
|
|
lval->data_type(),
|
2008-09-26 06:22:21 +02:00
|
|
|
lval->vector_width(), pin(1));
|
2000-09-07 03:29:44 +02:00
|
|
|
|
2008-08-12 06:21:33 +02:00
|
|
|
if (rval_expr == 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: Unable to elaborate r-value: "
|
|
|
|
|
<< *pin(1) << endl;
|
|
|
|
|
des->errors += 1;
|
2000-05-08 07:28:29 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-22 22:48:34 +01:00
|
|
|
NetNet*rval = rval_expr->synthesize(des, scope, rval_expr);
|
2008-08-12 06:21:33 +02:00
|
|
|
|
1999-05-20 06:31:45 +02:00
|
|
|
if (rval == 0) {
|
2008-08-12 06:21:33 +02:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "Failed to synthesize expression: " << *rval_expr << endl;
|
1999-05-20 06:31:45 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-30 02:42:05 +01:00
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: PGAssign: elaborated r-value"
|
2008-08-12 06:21:33 +02:00
|
|
|
<< " width="<< rval->vector_width()
|
2008-08-16 06:19:04 +02:00
|
|
|
<< ", type="<< rval->data_type()
|
|
|
|
|
<< ", expr=" << *rval_expr << endl;
|
2005-01-30 02:42:05 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-19 08:34:27 +01:00
|
|
|
ivl_assert(*this, lval && rval);
|
|
|
|
|
ivl_assert(*this, rval->pin_count() == 1);
|
2001-11-05 00:12:29 +01:00
|
|
|
|
2008-08-13 06:03:38 +02:00
|
|
|
// Detect the case that the rvalue-expression is a simple
|
|
|
|
|
// expression. In this case, we will need to create a driver
|
|
|
|
|
// (later) to carry strengths.
|
|
|
|
|
bool need_driver_flag = false;
|
|
|
|
|
if (dynamic_cast<NetESignal*>(rval_expr))
|
|
|
|
|
need_driver_flag = true;
|
|
|
|
|
|
2013-12-19 08:34:27 +01:00
|
|
|
// expression elaboration should have caused the rval width to
|
|
|
|
|
// match the l-value by now.
|
|
|
|
|
if (rval->vector_width() < lval->vector_width()) {
|
|
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "lval-rval width mismatch: "
|
|
|
|
|
<< "rval->vector_width()==" << rval->vector_width()
|
|
|
|
|
<< ", lval->vector_width()==" << lval->vector_width() << endl;
|
2008-08-15 05:38:34 +02:00
|
|
|
}
|
2013-03-01 22:34:42 +01:00
|
|
|
ivl_assert(*this, rval->vector_width() >= lval->vector_width());
|
1999-06-13 01:16:37 +02:00
|
|
|
|
2013-03-01 22:34:42 +01:00
|
|
|
/* If the r-value insists on being larger than the l-value,
|
|
|
|
|
use a part select to chop it down down to size. */
|
2006-04-26 06:43:50 +02:00
|
|
|
if (lval->vector_width() < rval->vector_width()) {
|
|
|
|
|
NetPartSelect*tmp = new NetPartSelect(rval, 0,lval->vector_width(),
|
|
|
|
|
NetPartSelect::VP);
|
|
|
|
|
des->add_node(tmp);
|
|
|
|
|
tmp->set_line(*this);
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*osig_vec = new netvector_t(rval->data_type(),
|
|
|
|
|
lval->vector_width()-1,0);
|
2006-04-26 06:43:50 +02:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::TRI, osig_vec);
|
2006-04-26 06:43:50 +02:00
|
|
|
osig->set_line(*this);
|
2008-08-13 06:31:39 +02:00
|
|
|
osig->local_flag(true);
|
2006-04-26 06:43:50 +02:00
|
|
|
connect(osig->pin(0), tmp->pin(0));
|
|
|
|
|
rval = osig;
|
2008-08-13 06:03:38 +02:00
|
|
|
need_driver_flag = false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-13 03:34:46 +01:00
|
|
|
/* When we are given a non-default strength value and if the drive
|
|
|
|
|
* source is a bit, part, indexed select or a concatenation we need
|
|
|
|
|
* to add a driver (BUFZ) to convey the strength information. */
|
2010-03-16 23:16:53 +01:00
|
|
|
if ((drive0 != IVL_DR_STRONG || drive1 != IVL_DR_STRONG) &&
|
2013-02-13 03:37:41 +01:00
|
|
|
((dynamic_cast<NetESelect*>(rval_expr)) ||
|
2013-02-13 03:34:46 +01:00
|
|
|
(dynamic_cast<NetEConcat*>(rval_expr)))) {
|
2009-04-03 02:20:41 +02:00
|
|
|
need_driver_flag = true;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-13 06:03:38 +02:00
|
|
|
if (need_driver_flag) {
|
|
|
|
|
NetBUFZ*driver = new NetBUFZ(scope, scope->local_symbol(),
|
2010-07-12 02:16:15 +02:00
|
|
|
rval->vector_width(), false);
|
2008-08-13 06:03:38 +02:00
|
|
|
driver->set_line(*this);
|
|
|
|
|
des->add_node(driver);
|
|
|
|
|
|
|
|
|
|
connect(rval->pin(0), driver->pin(1));
|
|
|
|
|
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*tmp_vec = new netvector_t(rval->data_type(),
|
|
|
|
|
rval->vector_width()-1,0);
|
2008-08-13 06:03:38 +02:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::WIRE, tmp_vec);
|
2008-08-13 06:03:38 +02:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->local_flag(true);
|
|
|
|
|
|
|
|
|
|
connect(driver->pin(0), tmp->pin(0));
|
|
|
|
|
|
|
|
|
|
rval = tmp;
|
2006-04-26 06:43:50 +02:00
|
|
|
}
|
|
|
|
|
|
2008-08-12 06:21:33 +02:00
|
|
|
/* Set the drive and delays for the r-val. */
|
|
|
|
|
|
2010-03-16 23:16:53 +01:00
|
|
|
if (drive0 != IVL_DR_STRONG || drive1 != IVL_DR_STRONG)
|
2008-08-12 06:21:33 +02:00
|
|
|
rval->pin(0).drivers_drive(drive0, drive1);
|
|
|
|
|
|
|
|
|
|
if (rise_time || fall_time || decay_time)
|
2008-02-02 05:13:23 +01:00
|
|
|
rval->pin(0).drivers_delays(rise_time, fall_time, decay_time);
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
connect(lval->pin(0), rval->pin(0));
|
1999-10-08 19:27:23 +02:00
|
|
|
|
|
|
|
|
if (lval->local_flag())
|
|
|
|
|
delete lval;
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2014-03-17 01:08:38 +01:00
|
|
|
void PGAssign::elaborate_unpacked_array_(Design*des, NetScope*scope, NetNet*lval) const
|
|
|
|
|
{
|
|
|
|
|
PEIdent*rval_pident = dynamic_cast<PEIdent*> (pin(1));
|
|
|
|
|
ivl_assert(*this, rval_pident);
|
|
|
|
|
|
|
|
|
|
NetNet*rval_net = rval_pident->elaborate_unpacked_net(des, scope);
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, rval_net->pin_count() == lval->pin_count());
|
|
|
|
|
|
2014-03-23 04:50:47 +01:00
|
|
|
assign_unpacked_with_bufz(des, scope, this, lval, rval_net);
|
2014-03-17 01:08:38 +01:00
|
|
|
}
|
|
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
unsigned PGBuiltin::calculate_array_count_(Design*des, NetScope*scope,
|
|
|
|
|
long&high, long&low) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-02-15 03:06:15 +01:00
|
|
|
unsigned count = 1;
|
2008-09-15 06:04:03 +02:00
|
|
|
high = 0;
|
|
|
|
|
low = 0;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
2003-01-27 06:09:17 +01:00
|
|
|
/* If the Verilog source has a range specification for the
|
2007-03-22 17:08:14 +01:00
|
|
|
gates, then I am expected to make more than one
|
1999-02-15 03:06:15 +01:00
|
|
|
gate. Figure out how many are desired. */
|
|
|
|
|
if (msb_) {
|
2011-03-27 12:08:33 +02:00
|
|
|
NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1, true);
|
|
|
|
|
NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1, true);
|
1999-02-15 03:06:15 +01:00
|
|
|
|
2003-09-20 08:00:37 +02:00
|
|
|
NetEConst*msb_con = dynamic_cast<NetEConst*>(msb_exp);
|
|
|
|
|
NetEConst*lsb_con = dynamic_cast<NetEConst*>(lsb_exp);
|
|
|
|
|
|
|
|
|
|
if (msb_con == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Unable to evaluate "
|
1999-10-07 07:25:33 +02:00
|
|
|
"expression " << *msb_ << endl;
|
1999-02-15 03:06:15 +01:00
|
|
|
des->errors += 1;
|
2008-09-15 06:04:03 +02:00
|
|
|
return 0;
|
1999-02-15 03:06:15 +01:00
|
|
|
}
|
|
|
|
|
|
2003-09-20 08:00:37 +02:00
|
|
|
if (lsb_con == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Unable to evaluate "
|
1999-10-07 07:25:33 +02:00
|
|
|
"expression " << *lsb_ << endl;
|
1999-02-15 03:06:15 +01:00
|
|
|
des->errors += 1;
|
2008-09-15 06:04:03 +02:00
|
|
|
return 0;
|
1999-02-15 03:06:15 +01:00
|
|
|
}
|
|
|
|
|
|
2003-09-20 08:00:37 +02:00
|
|
|
verinum msb = msb_con->value();
|
|
|
|
|
verinum lsb = lsb_con->value();
|
|
|
|
|
|
|
|
|
|
delete msb_exp;
|
|
|
|
|
delete lsb_exp;
|
|
|
|
|
|
|
|
|
|
if (msb.as_long() > lsb.as_long())
|
|
|
|
|
count = msb.as_long() - lsb.as_long() + 1;
|
1999-02-15 03:06:15 +01:00
|
|
|
else
|
2003-09-20 08:00:37 +02:00
|
|
|
count = lsb.as_long() - msb.as_long() + 1;
|
1999-02-15 03:06:15 +01:00
|
|
|
|
2003-09-20 08:00:37 +02:00
|
|
|
low = lsb.as_long();
|
|
|
|
|
high = msb.as_long();
|
2004-12-30 00:55:43 +01:00
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
2008-06-12 19:04:29 +02:00
|
|
|
cerr << get_fileline() << ": debug: PGBuiltin: Make array "
|
2011-04-04 19:43:58 +02:00
|
|
|
<< "[" << high << ":" << low << "]" << " of "
|
|
|
|
|
<< count << " gates for " << get_name() << endl;
|
2004-12-30 00:55:43 +01:00
|
|
|
}
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-09 09:39:03 +02:00
|
|
|
void PGBuiltin::calculate_gate_and_lval_count_(unsigned&gate_count,
|
|
|
|
|
unsigned&lval_count) const
|
2008-09-15 06:04:03 +02:00
|
|
|
{
|
|
|
|
|
switch (type()) {
|
|
|
|
|
case BUF:
|
|
|
|
|
case NOT:
|
2011-06-09 09:39:03 +02:00
|
|
|
if (pin_count() > 2) gate_count = pin_count() - 1;
|
|
|
|
|
else gate_count = 1;
|
|
|
|
|
lval_count = gate_count;
|
2008-09-15 06:04:03 +02:00
|
|
|
break;
|
|
|
|
|
case PULLDOWN:
|
|
|
|
|
case PULLUP:
|
2011-06-09 09:39:03 +02:00
|
|
|
gate_count = pin_count();
|
|
|
|
|
lval_count = gate_count;
|
|
|
|
|
break;
|
|
|
|
|
case TRAN:
|
|
|
|
|
case RTRAN:
|
|
|
|
|
case TRANIF0:
|
|
|
|
|
case TRANIF1:
|
|
|
|
|
case RTRANIF0:
|
|
|
|
|
case RTRANIF1:
|
|
|
|
|
gate_count = 1;
|
|
|
|
|
lval_count = 2;
|
2008-09-15 06:04:03 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
2011-06-09 09:39:03 +02:00
|
|
|
gate_count = 1;
|
|
|
|
|
lval_count = 1;
|
2008-09-15 06:04:03 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNode* PGBuiltin::create_gate_for_output_(Design*des, NetScope*scope,
|
2010-07-13 19:01:32 +02:00
|
|
|
perm_string inst_name,
|
2008-09-15 06:04:03 +02:00
|
|
|
unsigned instance_width) const
|
|
|
|
|
{
|
|
|
|
|
NetNode*gate = 0;
|
|
|
|
|
|
|
|
|
|
switch (type()) {
|
|
|
|
|
|
|
|
|
|
case AND:
|
|
|
|
|
if (pin_count() < 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: the AND "
|
|
|
|
|
"primitive must have an input." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::AND, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BUF:
|
2008-12-08 19:55:02 +01:00
|
|
|
if (pin_count() < 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: the BUF "
|
|
|
|
|
"primitive must have an input." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, 2,
|
2008-12-08 19:55:02 +01:00
|
|
|
NetLogic::BUF, instance_width);
|
|
|
|
|
}
|
2008-09-15 06:04:03 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BUFIF0:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: the BUFIF0 "
|
|
|
|
|
"primitive must have three arguments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::BUFIF0, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BUFIF1:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: the BUFIF1 "
|
|
|
|
|
"primitive must have three arguments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::BUFIF1, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CMOS:
|
|
|
|
|
if (pin_count() != 4) {
|
|
|
|
|
cerr << get_fileline() << ": error: the CMOS "
|
|
|
|
|
"primitive must have four arguments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::CMOS, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NAND:
|
|
|
|
|
if (pin_count() < 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: the NAND "
|
|
|
|
|
"primitive must have an input." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::NAND, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NMOS:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: the NMOS "
|
|
|
|
|
"primitive must have three arguments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::NMOS, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NOR:
|
|
|
|
|
if (pin_count() < 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: the NOR "
|
|
|
|
|
"primitive must have an input." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::NOR, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NOT:
|
2008-12-08 19:55:02 +01:00
|
|
|
if (pin_count() < 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: the NOT "
|
|
|
|
|
"primitive must have an input." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, 2,
|
2008-12-08 19:55:02 +01:00
|
|
|
NetLogic::NOT, instance_width);
|
|
|
|
|
}
|
2008-09-15 06:04:03 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NOTIF0:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: the NOTIF0 "
|
|
|
|
|
"primitive must have three arguments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::NOTIF0, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NOTIF1:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: the NOTIF1 "
|
|
|
|
|
"primitive must have three arguments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::NOTIF1, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OR:
|
|
|
|
|
if (pin_count() < 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: the OR "
|
|
|
|
|
"primitive must have an input." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::OR, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RCMOS:
|
|
|
|
|
if (pin_count() != 4) {
|
|
|
|
|
cerr << get_fileline() << ": error: the RCMOS "
|
|
|
|
|
"primitive must have four arguments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::RCMOS, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
2004-12-30 00:55:43 +01:00
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
case RNMOS:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: the RNMOS "
|
|
|
|
|
"primitive must have three arguments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::RNMOS, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RPMOS:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: the RPMOS "
|
|
|
|
|
"primitive must have three arguments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::RPMOS, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PMOS:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: the PMOS "
|
|
|
|
|
"primitive must have three arguments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::PMOS, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PULLDOWN:
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, 1,
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::PULLDOWN, instance_width);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PULLUP:
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, 1,
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::PULLUP, instance_width);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case XNOR:
|
|
|
|
|
if (pin_count() < 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: the XNOR "
|
|
|
|
|
"primitive must have an input." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::XNOR, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case XOR:
|
|
|
|
|
if (pin_count() < 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: the XOR "
|
|
|
|
|
"primitive must have an input." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2010-07-13 19:01:32 +02:00
|
|
|
gate = new NetLogic(scope, inst_name, pin_count(),
|
2008-09-15 06:04:03 +02:00
|
|
|
NetLogic::XOR, instance_width);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TRAN:
|
|
|
|
|
if (pin_count() != 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: Pin count for "
|
|
|
|
|
<< "tran device." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2012-01-02 19:14:20 +01:00
|
|
|
gate = new NetTran(scope, inst_name, IVL_SW_TRAN,
|
|
|
|
|
instance_width);
|
2008-09-15 06:04:03 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RTRAN:
|
|
|
|
|
if (pin_count() != 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: Pin count for "
|
|
|
|
|
<< "rtran device." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2012-01-02 19:14:20 +01:00
|
|
|
gate = new NetTran(scope, inst_name, IVL_SW_RTRAN,
|
|
|
|
|
instance_width);
|
2008-09-15 06:04:03 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TRANIF0:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: Pin count for "
|
|
|
|
|
<< "tranif0 device." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2012-01-02 19:14:20 +01:00
|
|
|
gate = new NetTran(scope, inst_name, IVL_SW_TRANIF0,
|
|
|
|
|
instance_width);
|
2008-09-15 06:04:03 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RTRANIF0:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: Pin count for "
|
|
|
|
|
<< "rtranif0 device." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2012-01-02 19:14:20 +01:00
|
|
|
gate = new NetTran(scope, inst_name, IVL_SW_RTRANIF0,
|
|
|
|
|
instance_width);
|
2008-09-15 06:04:03 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TRANIF1:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: Pin count for "
|
|
|
|
|
<< "tranif1 device." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2012-01-02 19:14:20 +01:00
|
|
|
gate = new NetTran(scope, inst_name, IVL_SW_TRANIF1,
|
|
|
|
|
instance_width);
|
2008-09-15 06:04:03 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RTRANIF1:
|
|
|
|
|
if (pin_count() != 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: Pin count for "
|
|
|
|
|
<< "rtranif1 device." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2012-01-02 19:14:20 +01:00
|
|
|
gate = new NetTran(scope, inst_name, IVL_SW_RTRANIF1,
|
|
|
|
|
instance_width);
|
2008-09-15 06:04:03 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
cerr << get_fileline() << ": internal error: unhandled "
|
|
|
|
|
"gate type." << endl;
|
2007-08-30 02:29:56 +02:00
|
|
|
des->errors += 1;
|
2008-09-15 06:04:03 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return gate;
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-13 19:01:32 +02:00
|
|
|
bool PGBuiltin::check_delay_count(Design*des) const
|
|
|
|
|
{
|
|
|
|
|
switch (type()) {
|
|
|
|
|
case AND:
|
|
|
|
|
case NAND:
|
|
|
|
|
case OR:
|
|
|
|
|
case NOR:
|
|
|
|
|
case XOR:
|
|
|
|
|
case XNOR:
|
|
|
|
|
case BUF:
|
|
|
|
|
case NOT:
|
|
|
|
|
if (delay_count() > 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: More than two delays "
|
|
|
|
|
<< "given to a " << gate_name() << " gate." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BUFIF0:
|
|
|
|
|
case NOTIF0:
|
|
|
|
|
case BUFIF1:
|
|
|
|
|
case NOTIF1:
|
|
|
|
|
if (delay_count() > 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: More than three delays "
|
|
|
|
|
<< "given to a " << gate_name() << " gate." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NMOS:
|
|
|
|
|
case RNMOS:
|
|
|
|
|
case PMOS:
|
|
|
|
|
case RPMOS:
|
|
|
|
|
case CMOS:
|
|
|
|
|
case RCMOS:
|
|
|
|
|
if (delay_count() > 3) {
|
|
|
|
|
cerr << get_fileline() << ": error: More than three delays "
|
|
|
|
|
<< "given to a " << gate_name() << " switch." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TRAN:
|
|
|
|
|
case RTRAN:
|
|
|
|
|
if (delay_count() != 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: A " << gate_name()
|
|
|
|
|
<< " switch does not take any delays." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TRANIF0:
|
|
|
|
|
case TRANIF1:
|
|
|
|
|
if (delay_count() > 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: More than two delays "
|
|
|
|
|
<< "given to a " << gate_name() << " switch." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RTRANIF0:
|
|
|
|
|
case RTRANIF1:
|
|
|
|
|
if (delay_count() > 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: More than two delays "
|
|
|
|
|
<< "given to an " << gate_name() << " switch." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PULLUP:
|
|
|
|
|
case PULLDOWN:
|
|
|
|
|
if (delay_count() != 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: A " << gate_name()
|
|
|
|
|
<< " source does not take any delays." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
cerr << get_fileline() << ": internal error: unhandled "
|
|
|
|
|
"gate type." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
/*
|
|
|
|
|
* Elaborate a Builtin gate. These normally get translated into
|
|
|
|
|
* NetLogic nodes that reflect the particular logic function.
|
|
|
|
|
*/
|
|
|
|
|
void PGBuiltin::elaborate(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
unsigned instance_width = 1;
|
|
|
|
|
perm_string name = get_name();
|
|
|
|
|
|
2011-04-04 19:43:58 +02:00
|
|
|
if (name == "") name = scope->local_symbol();
|
2008-09-15 06:04:03 +02:00
|
|
|
|
|
|
|
|
/* Calculate the array bounds and instance count for the gate,
|
|
|
|
|
as described in the Verilog source. If there is none, then
|
|
|
|
|
the count is 1, and high==low==0. */
|
|
|
|
|
|
|
|
|
|
long low=0, high=0;
|
|
|
|
|
unsigned array_count = calculate_array_count_(des, scope, high, low);
|
2011-04-04 19:43:58 +02:00
|
|
|
if (array_count == 0) return;
|
2008-09-15 06:04:03 +02:00
|
|
|
|
2011-06-09 09:39:03 +02:00
|
|
|
unsigned gate_count = 0, lval_count = 0;
|
|
|
|
|
calculate_gate_and_lval_count_(gate_count, lval_count);
|
2008-09-15 06:04:03 +02:00
|
|
|
|
2011-06-09 09:39:03 +02:00
|
|
|
/* Now we have a gate count. Elaborate the lval (output or
|
|
|
|
|
bi-directional) expressions only. We do it early so that
|
|
|
|
|
we can see if we can make wide gates instead of an array
|
|
|
|
|
of gates. */
|
2008-09-15 06:04:03 +02:00
|
|
|
|
2011-06-09 09:39:03 +02:00
|
|
|
vector<NetNet*>lval_sigs (lval_count);
|
2008-09-15 06:04:03 +02:00
|
|
|
|
2011-06-09 09:39:03 +02:00
|
|
|
for (unsigned idx = 0 ; idx < lval_count ; idx += 1) {
|
2008-09-15 06:04:03 +02:00
|
|
|
if (pin(idx) == 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: Logic gate port "
|
|
|
|
|
"expressions are not optional." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-06-09 09:39:03 +02:00
|
|
|
if (lval_count > gate_count)
|
|
|
|
|
lval_sigs[idx] = pin(idx)->elaborate_bi_net(des, scope);
|
|
|
|
|
else
|
|
|
|
|
lval_sigs[idx] = pin(idx)->elaborate_lnet(des, scope);
|
|
|
|
|
|
2009-04-02 03:31:29 +02:00
|
|
|
// The only way this should return zero is if an error
|
|
|
|
|
// happened, so for that case just return.
|
|
|
|
|
if (lval_sigs[idx] == 0) return;
|
2008-09-15 06:04:03 +02:00
|
|
|
|
|
|
|
|
// For now, assume all the outputs are the same width.
|
|
|
|
|
ivl_assert(*this, idx == 0 || lval_sigs[idx]->vector_width() == lval_sigs[0]->vector_width());
|
2007-08-30 02:29:56 +02:00
|
|
|
}
|
2004-12-30 00:55:43 +01:00
|
|
|
|
|
|
|
|
/* Detect the special case that the l-value width exactly
|
|
|
|
|
matches the gate count. In this case, we will make a single
|
2008-09-15 06:04:03 +02:00
|
|
|
gate that has the desired vector width.
|
|
|
|
|
|
|
|
|
|
NOTE: This assumes that all the outputs have the same
|
|
|
|
|
width. For gates with 1 output, this is trivially true. */
|
|
|
|
|
if (lval_sigs[0]->vector_width() == array_count) {
|
|
|
|
|
instance_width = array_count;
|
|
|
|
|
array_count = 1;
|
2004-12-30 00:55:43 +01:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
if (debug_elaborate && instance_width != 1)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: PGBuiltin: "
|
2004-12-30 00:55:43 +01:00
|
|
|
"Collapsed gate array into single wide "
|
|
|
|
|
"(" << instance_width << ") instance." << endl;
|
|
|
|
|
}
|
1999-02-15 03:06:15 +01:00
|
|
|
|
1999-08-01 18:34:50 +02:00
|
|
|
/* Calculate the gate delays from the delay expressions
|
|
|
|
|
given in the source. For logic gates, the decay time
|
|
|
|
|
is meaningless because it can never go to high
|
2003-01-27 06:09:17 +01:00
|
|
|
impedance. However, the bufif devices can generate
|
1999-08-01 18:34:50 +02:00
|
|
|
'bz output, so we will pretend that anything can.
|
|
|
|
|
|
2003-01-27 06:09:17 +01:00
|
|
|
If only one delay value expression is given (i.e., #5
|
1999-08-01 18:34:50 +02:00
|
|
|
nand(foo,...)) then rise, fall and decay times are
|
|
|
|
|
all the same value. If two values are given, rise and
|
|
|
|
|
fall times are use, and the decay time is the minimum
|
|
|
|
|
of the rise and fall times. Finally, if all three
|
|
|
|
|
values are given, they are taken as specified. */
|
|
|
|
|
|
2010-07-13 19:01:32 +02:00
|
|
|
if (check_delay_count(des)) return;
|
2006-01-02 06:33:19 +01:00
|
|
|
NetExpr* rise_time, *fall_time, *decay_time;
|
2001-11-22 07:20:59 +01:00
|
|
|
eval_delays(des, scope, rise_time, fall_time, decay_time);
|
1999-08-01 18:34:50 +02:00
|
|
|
|
2012-11-13 03:13:41 +01:00
|
|
|
struct attrib_list_t*attrib_list;
|
2002-05-23 05:08:50 +02:00
|
|
|
unsigned attrib_list_n = 0;
|
|
|
|
|
attrib_list = evaluate_attributes(attributes, attrib_list_n,
|
|
|
|
|
des, scope);
|
|
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
/* Allocate all the netlist nodes for the gates. */
|
2011-06-09 09:39:03 +02:00
|
|
|
vector<NetNode*>cur (array_count*gate_count);
|
2008-09-15 06:04:03 +02:00
|
|
|
|
1999-08-01 18:34:50 +02:00
|
|
|
/* Now make as many gates as the bit count dictates. Give each
|
|
|
|
|
a unique name, and set the delay times. */
|
|
|
|
|
|
2011-06-09 09:39:03 +02:00
|
|
|
for (unsigned idx = 0 ; idx < array_count*gate_count ; idx += 1) {
|
|
|
|
|
unsigned array_idx = idx/gate_count;
|
|
|
|
|
unsigned gate_idx = idx%gate_count;
|
2008-09-15 06:04:03 +02:00
|
|
|
|
2003-01-14 22:16:18 +01:00
|
|
|
ostringstream tmp;
|
2008-09-15 06:04:03 +02:00
|
|
|
unsigned index = (low < high)? (low+array_idx) : (low-array_idx);
|
1999-02-15 03:06:15 +01:00
|
|
|
|
2011-06-09 09:39:03 +02:00
|
|
|
tmp << name << "<" << index << "." << gate_idx << ">";
|
2004-02-18 18:11:54 +01:00
|
|
|
perm_string inm = lex_strings.make(tmp.str());
|
1999-02-15 03:06:15 +01:00
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
cur[idx] = create_gate_for_output_(des, scope, inm, instance_width);
|
|
|
|
|
if (cur[idx] == 0)
|
2000-01-09 06:50:48 +01:00
|
|
|
return;
|
1999-02-15 03:06:15 +01:00
|
|
|
|
2002-05-23 05:08:50 +02:00
|
|
|
for (unsigned adx = 0 ; adx < attrib_list_n ; adx += 1)
|
|
|
|
|
cur[idx]->attribute(attrib_list[adx].key,
|
|
|
|
|
attrib_list[adx].val);
|
|
|
|
|
|
2010-07-13 19:01:32 +02:00
|
|
|
/* Set the delays and drive strength for all built in gates. */
|
2010-07-13 01:04:22 +02:00
|
|
|
cur[idx]->rise_time(rise_time);
|
|
|
|
|
cur[idx]->fall_time(fall_time);
|
|
|
|
|
cur[idx]->decay_time(decay_time);
|
|
|
|
|
|
|
|
|
|
cur[idx]->pin(0).drive0(strength0());
|
|
|
|
|
cur[idx]->pin(0).drive1(strength1());
|
2000-05-08 07:28:29 +02:00
|
|
|
|
2008-05-20 06:42:52 +02:00
|
|
|
cur[idx]->set_line(*this);
|
1999-02-15 03:06:15 +01:00
|
|
|
des->add_node(cur[idx]);
|
|
|
|
|
}
|
|
|
|
|
|
2002-05-23 05:08:50 +02:00
|
|
|
|
|
|
|
|
delete[]attrib_list;
|
|
|
|
|
|
1999-02-15 03:06:15 +01:00
|
|
|
/* The gates have all been allocated, this loop runs through
|
|
|
|
|
the parameters and attaches the ports of the objects. */
|
1998-11-04 00:28:49 +01:00
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < pin_count() ; idx += 1) {
|
2008-09-15 06:04:03 +02:00
|
|
|
|
2008-11-27 00:37:38 +01:00
|
|
|
PExpr*ex = pin(idx);
|
2007-08-30 02:29:56 +02:00
|
|
|
if (ex == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Logic gate port "
|
2007-08-30 02:29:56 +02:00
|
|
|
"expressions are not optional." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2008-09-09 04:13:49 +02:00
|
|
|
NetNet*sig = 0;
|
2011-06-09 09:39:03 +02:00
|
|
|
if (idx < lval_count) {
|
2008-09-15 06:04:03 +02:00
|
|
|
sig = lval_sigs[idx];
|
2008-09-09 04:13:49 +02:00
|
|
|
|
|
|
|
|
} else {
|
2011-05-30 10:25:54 +02:00
|
|
|
// If this is an array, the port expression is required
|
|
|
|
|
// to be the exact width required (this will be checked
|
|
|
|
|
// later). But if this is a single instance, consensus
|
|
|
|
|
// is that we just take the LSB of the port expression.
|
|
|
|
|
NetExpr*tmp = elab_and_eval(des, scope, ex, msb_ ? -1 : 1);
|
|
|
|
|
if (tmp == 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (msb_ == 0 && tmp->expr_width() != 1)
|
|
|
|
|
tmp = new NetESelect(tmp, make_const_0(1), 1,
|
|
|
|
|
IVL_SEL_IDX_UP);
|
2008-12-22 22:48:34 +01:00
|
|
|
sig = tmp->synthesize(des, scope, tmp);
|
2008-09-09 04:13:49 +02:00
|
|
|
delete tmp;
|
|
|
|
|
}
|
|
|
|
|
|
1999-09-14 03:50:52 +02:00
|
|
|
if (sig == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
ivl_assert(*this, sig);
|
1999-02-15 03:06:15 +01:00
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
if (array_count == 1) {
|
2004-12-30 00:55:43 +01:00
|
|
|
/* Handle the case where there is one gate that
|
|
|
|
|
carries the whole vector width. */
|
2005-02-08 01:12:36 +01:00
|
|
|
|
2006-09-23 00:14:27 +02:00
|
|
|
if (1 == sig->vector_width() && instance_width != 1) {
|
2005-02-08 01:12:36 +01:00
|
|
|
|
|
|
|
|
assert(sig->vector_width() == 1);
|
|
|
|
|
NetReplicate*rep
|
|
|
|
|
= new NetReplicate(scope,
|
|
|
|
|
scope->local_symbol(),
|
|
|
|
|
instance_width,
|
|
|
|
|
instance_width);
|
|
|
|
|
rep->set_line(*this);
|
|
|
|
|
des->add_node(rep);
|
|
|
|
|
connect(rep->pin(1), sig->pin(0));
|
|
|
|
|
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*osig_vec = new netvector_t(IVL_VT_LOGIC,
|
|
|
|
|
instance_width-1,0);
|
2005-02-08 01:12:36 +01:00
|
|
|
sig = new NetNet(scope, scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::WIRE, osig_vec);
|
2011-02-10 06:03:08 +01:00
|
|
|
sig->set_line(*this);
|
2005-02-08 01:12:36 +01:00
|
|
|
sig->local_flag(true);
|
|
|
|
|
connect(rep->pin(0), sig->pin(0));
|
2006-09-23 00:14:27 +02:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (instance_width != sig->vector_width()) {
|
|
|
|
|
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: "
|
2006-09-23 00:14:27 +02:00
|
|
|
<< "Expression width " << sig->vector_width()
|
|
|
|
|
<< " does not match width " << instance_width
|
2011-05-23 06:40:54 +02:00
|
|
|
<< " of logic gate array port " << idx+1
|
2006-09-23 00:14:27 +02:00
|
|
|
<< "." << endl;
|
|
|
|
|
des->errors += 1;
|
2005-02-08 01:12:36 +01:00
|
|
|
}
|
|
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
// There is only 1 instance, but there may be
|
|
|
|
|
// multiple outputs to that gate. That would
|
|
|
|
|
// potentially mean multiple actual gates.
|
|
|
|
|
// Although in Verilog proper a multiple
|
|
|
|
|
// output gate has only 1 input, this conditional
|
|
|
|
|
// handles gates with N outputs and M inputs.
|
2011-06-09 09:39:03 +02:00
|
|
|
if (idx < gate_count) {
|
2008-09-15 06:04:03 +02:00
|
|
|
connect(cur[idx]->pin(0), sig->pin(0));
|
|
|
|
|
} else {
|
2011-06-09 09:39:03 +02:00
|
|
|
for (unsigned dev = 0 ; dev < gate_count; dev += 1)
|
|
|
|
|
connect(cur[dev]->pin(idx-gate_count+1), sig->pin(0));
|
2008-09-15 06:04:03 +02:00
|
|
|
}
|
2004-12-30 00:55:43 +01:00
|
|
|
|
|
|
|
|
} else if (sig->vector_width() == 1) {
|
2008-09-15 06:04:03 +02:00
|
|
|
|
2004-12-30 00:55:43 +01:00
|
|
|
/* Handle the case where a single bit is connected
|
2008-09-17 06:03:26 +02:00
|
|
|
repetitively to all the instances. If idx is an
|
|
|
|
|
output port, connect it to all array_count
|
|
|
|
|
devices that have outputs at this
|
|
|
|
|
position. Otherwise, idx is an input to all
|
2011-06-09 09:39:03 +02:00
|
|
|
array_count*gate_count devices. */
|
2008-09-17 06:03:26 +02:00
|
|
|
|
2011-06-09 09:39:03 +02:00
|
|
|
if (idx < gate_count) {
|
2008-09-17 06:03:26 +02:00
|
|
|
for (unsigned gdx = 0 ; gdx < array_count ; gdx += 1) {
|
2011-06-09 09:39:03 +02:00
|
|
|
unsigned dev = gdx*gate_count;
|
2008-09-17 06:03:26 +02:00
|
|
|
connect(cur[dev+idx]->pin(0), sig->pin(0));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2011-06-09 09:39:03 +02:00
|
|
|
unsigned use_idx = idx - gate_count + 1;
|
2008-09-17 06:03:26 +02:00
|
|
|
for (unsigned gdx = 0 ; gdx < cur.size() ; gdx += 1)
|
|
|
|
|
connect(cur[gdx]->pin(use_idx), sig->pin(0));
|
|
|
|
|
}
|
1999-02-15 03:06:15 +01:00
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
} else if (sig->vector_width() == array_count) {
|
2004-12-30 00:55:43 +01:00
|
|
|
|
2011-06-09 09:39:03 +02:00
|
|
|
/* Bi-directional switches should get collapsed into
|
|
|
|
|
a single wide instance, so should never reach this
|
|
|
|
|
point. Check this is so, as the following code
|
|
|
|
|
doesn't handle bi-directional connections. */
|
|
|
|
|
ivl_assert(*this, lval_count == gate_count);
|
|
|
|
|
|
2004-12-30 00:55:43 +01:00
|
|
|
/* Handle the general case that each bit of the
|
|
|
|
|
value is connected to a different instance. In
|
|
|
|
|
this case, the output is handled slightly
|
|
|
|
|
different from the inputs. */
|
2011-06-09 09:39:03 +02:00
|
|
|
if (idx < gate_count) {
|
2004-12-30 00:55:43 +01:00
|
|
|
NetConcat*cc = new NetConcat(scope,
|
|
|
|
|
scope->local_symbol(),
|
|
|
|
|
sig->vector_width(),
|
2008-09-15 06:04:03 +02:00
|
|
|
array_count);
|
2011-02-10 06:03:08 +01:00
|
|
|
cc->set_line(*this);
|
2004-12-30 00:55:43 +01:00
|
|
|
des->add_node(cc);
|
|
|
|
|
|
|
|
|
|
/* Connect the concat to the signal. */
|
|
|
|
|
connect(cc->pin(0), sig->pin(0));
|
|
|
|
|
|
|
|
|
|
/* Connect the outputs of the gates to the concat. */
|
2008-09-15 06:04:03 +02:00
|
|
|
for (unsigned gdx = 0 ; gdx < array_count; gdx += 1) {
|
2011-06-09 09:39:03 +02:00
|
|
|
unsigned dev = gdx*gate_count;
|
2008-09-17 06:03:26 +02:00
|
|
|
connect(cur[dev+idx]->pin(0), cc->pin(gdx+1));
|
2004-12-30 00:55:43 +01:00
|
|
|
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*tmp2_vec = new netvector_t(IVL_VT_LOGIC);
|
2004-12-30 00:55:43 +01:00
|
|
|
NetNet*tmp2 = new NetNet(scope,
|
|
|
|
|
scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::WIRE, tmp2_vec);
|
2011-02-10 06:03:08 +01:00
|
|
|
tmp2->set_line(*this);
|
2006-05-01 22:47:58 +02:00
|
|
|
tmp2->local_flag(true);
|
2004-12-30 00:55:43 +01:00
|
|
|
connect(cc->pin(gdx+1), tmp2->pin(0));
|
|
|
|
|
}
|
1999-02-15 03:06:15 +01:00
|
|
|
|
2008-09-15 06:04:03 +02:00
|
|
|
} else for (unsigned gdx = 0 ; gdx < array_count ; gdx += 1) {
|
2004-12-30 00:55:43 +01:00
|
|
|
/* Use part selects to get the bits
|
|
|
|
|
connected to the inputs of out gate. */
|
2005-01-09 21:16:00 +01:00
|
|
|
NetPartSelect*tmp1 = new NetPartSelect(sig, gdx, 1,
|
|
|
|
|
NetPartSelect::VP);
|
2004-12-30 00:55:43 +01:00
|
|
|
tmp1->set_line(*this);
|
|
|
|
|
des->add_node(tmp1);
|
|
|
|
|
connect(tmp1->pin(1), sig->pin(0));
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*tmp2_vec = new netvector_t(sig->data_type());
|
2004-12-30 00:55:43 +01:00
|
|
|
NetNet*tmp2 = new NetNet(scope, scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::WIRE, tmp2_vec);
|
2011-02-10 06:03:08 +01:00
|
|
|
tmp2->set_line(*this);
|
2006-05-01 22:47:58 +02:00
|
|
|
tmp2->local_flag(true);
|
2004-12-30 00:55:43 +01:00
|
|
|
connect(tmp1->pin(0), tmp2->pin(0));
|
2011-06-09 09:39:03 +02:00
|
|
|
unsigned use_idx = idx - gate_count + 1;
|
|
|
|
|
unsigned dev = gdx*gate_count;
|
|
|
|
|
for (unsigned gdx2 = 0 ; gdx2 < gate_count ; gdx2 += 1)
|
2008-09-17 06:03:26 +02:00
|
|
|
connect(cur[dev+gdx2]->pin(use_idx), tmp1->pin(0));
|
2004-12-30 00:55:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Gate count of " <<
|
2008-09-15 06:04:03 +02:00
|
|
|
array_count << " does not match net width of " <<
|
2004-12-30 00:55:43 +01:00
|
|
|
sig->vector_width() << " at pin " << idx << "."
|
1999-02-15 03:06:15 +01:00
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-02 04:45:12 +02:00
|
|
|
}
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2007-09-10 06:14:52 +02:00
|
|
|
NetNet*PGModule::resize_net_to_port_(Design*des, NetScope*scope,
|
|
|
|
|
NetNet*sig, unsigned port_wid,
|
2008-10-03 03:38:53 +02:00
|
|
|
NetNet::PortType dir, bool as_signed) const
|
2007-09-10 06:14:52 +02:00
|
|
|
{
|
|
|
|
|
ivl_assert(*this, dir != NetNet::NOT_A_PORT);
|
|
|
|
|
ivl_assert(*this, dir != NetNet::PIMPLICIT);
|
|
|
|
|
|
2012-09-30 00:13:45 +02:00
|
|
|
netvector_t*tmp_type = new netvector_t(IVL_VT_LOGIC, port_wid-1, 0);
|
2007-09-10 06:14:52 +02:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2012-09-30 00:13:45 +02:00
|
|
|
NetNet::WIRE, tmp_type);
|
2007-09-10 06:14:52 +02:00
|
|
|
tmp->local_flag(true);
|
2008-11-19 02:17:19 +01:00
|
|
|
tmp->set_line(*this);
|
2007-09-10 06:14:52 +02:00
|
|
|
|
2008-06-03 20:16:25 +02:00
|
|
|
// Handle the special case of a bi-directional part
|
|
|
|
|
// select. Create a NetTran(VP) instead of a uni-directional
|
|
|
|
|
// NetPartSelect node.
|
|
|
|
|
if (dir == NetNet::PINOUT) {
|
|
|
|
|
unsigned wida = sig->vector_width();
|
|
|
|
|
unsigned widb = tmp->vector_width();
|
|
|
|
|
bool part_b = widb < wida;
|
2008-10-03 03:38:53 +02:00
|
|
|
// This needs to pad the value!
|
|
|
|
|
// Also delete the inout specific warning when this is fixed.
|
|
|
|
|
// It is located just before this routine is called.
|
2008-06-03 20:16:25 +02:00
|
|
|
NetTran*node = new NetTran(scope, scope->local_symbol(),
|
|
|
|
|
part_b? wida : widb,
|
|
|
|
|
part_b? widb : wida,
|
|
|
|
|
0);
|
|
|
|
|
if (part_b) {
|
|
|
|
|
connect(node->pin(0), sig->pin(0));
|
|
|
|
|
connect(node->pin(1), tmp->pin(0));
|
2008-06-04 02:23:01 +02:00
|
|
|
} else {
|
|
|
|
|
connect(node->pin(0), tmp->pin(0));
|
|
|
|
|
connect(node->pin(1), sig->pin(0));
|
2008-06-03 20:16:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node->set_line(*this);
|
|
|
|
|
des->add_node(node);
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-03 03:38:53 +02:00
|
|
|
unsigned pwidth = tmp->vector_width();
|
|
|
|
|
unsigned swidth = sig->vector_width();
|
2007-09-10 06:14:52 +02:00
|
|
|
switch (dir) {
|
|
|
|
|
case NetNet::POUTPUT:
|
2008-10-03 03:38:53 +02:00
|
|
|
if (pwidth > swidth) {
|
|
|
|
|
NetPartSelect*node = new NetPartSelect(tmp, 0, swidth,
|
2007-09-10 06:14:52 +02:00
|
|
|
NetPartSelect::VP);
|
2008-01-11 03:20:21 +01:00
|
|
|
connect(node->pin(0), sig->pin(0));
|
2008-10-03 03:38:53 +02:00
|
|
|
des->add_node(node);
|
2007-09-10 06:14:52 +02:00
|
|
|
} else {
|
2008-10-03 03:38:53 +02:00
|
|
|
NetNet*osig;
|
|
|
|
|
if (as_signed) {
|
2008-11-19 02:17:19 +01:00
|
|
|
osig = pad_to_width_signed(des, tmp, swidth, *this);
|
2008-10-03 03:38:53 +02:00
|
|
|
} else {
|
2008-11-19 02:17:19 +01:00
|
|
|
osig = pad_to_width(des, tmp, swidth, *this);
|
2008-10-03 03:38:53 +02:00
|
|
|
}
|
|
|
|
|
connect(osig->pin(0), sig->pin(0));
|
2007-09-10 06:14:52 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NetNet::PINPUT:
|
2008-10-03 03:38:53 +02:00
|
|
|
if (pwidth > swidth) {
|
|
|
|
|
delete tmp;
|
|
|
|
|
if (as_signed) {
|
2008-11-19 02:17:19 +01:00
|
|
|
tmp = pad_to_width_signed(des, sig, pwidth, *this);
|
2008-10-03 03:38:53 +02:00
|
|
|
} else {
|
2008-11-19 02:17:19 +01:00
|
|
|
tmp = pad_to_width(des, sig, pwidth, *this);
|
2008-10-03 03:38:53 +02:00
|
|
|
}
|
2007-09-10 06:14:52 +02:00
|
|
|
} else {
|
2008-10-03 03:38:53 +02:00
|
|
|
NetPartSelect*node = new NetPartSelect(sig, 0, pwidth,
|
2007-09-10 06:14:52 +02:00
|
|
|
NetPartSelect::VP);
|
2008-01-11 05:47:06 +01:00
|
|
|
connect(node->pin(0), tmp->pin(0));
|
2008-10-03 03:38:53 +02:00
|
|
|
des->add_node(node);
|
2007-09-10 06:14:52 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NetNet::PINOUT:
|
2008-06-03 20:16:25 +02:00
|
|
|
ivl_assert(*this, 0);
|
2007-09-10 06:14:52 +02:00
|
|
|
break;
|
|
|
|
|
|
2012-02-25 19:19:48 +01:00
|
|
|
case NetNet::PREF:
|
|
|
|
|
ivl_assert(*this, 0);
|
|
|
|
|
break;
|
|
|
|
|
|
2007-09-10 06:14:52 +02:00
|
|
|
default:
|
|
|
|
|
ivl_assert(*this, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-03 05:08:38 +01:00
|
|
|
static bool need_bufz_for_input_port(const vector<NetNet*>&prts)
|
2008-08-07 06:04:52 +02:00
|
|
|
{
|
|
|
|
|
if (prts[0]->port_type() != NetNet::PINPUT)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (prts[0]->pin(0).nexus()->drivers_present())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-27 22:41:54 +02:00
|
|
|
/*
|
|
|
|
|
* Convert a wire or tri to a tri0 or tri1 as needed to make
|
|
|
|
|
* an unconnected drive pull for floating inputs.
|
|
|
|
|
*/
|
|
|
|
|
static void convert_net(Design*des, const LineInfo *line,
|
|
|
|
|
NetNet *net, NetNet::Type type)
|
|
|
|
|
{
|
|
|
|
|
// If the types already match just return.
|
|
|
|
|
if (net->type() == type) return;
|
|
|
|
|
|
|
|
|
|
// We can only covert a wire or tri to have a default pull.
|
|
|
|
|
if (net->type() == NetNet::WIRE || net->type() == NetNet::TRI) {
|
|
|
|
|
net->type(type);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We may have to support this at some point in time!
|
|
|
|
|
cerr << line->get_fileline() << ": sorry: Can not pull floating "
|
|
|
|
|
"input type '" << net->type() << "'." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
/*
|
|
|
|
|
* Instantiate a module by recursively elaborating it. Set the path of
|
|
|
|
|
* the recursive elaboration so that signal names get properly
|
|
|
|
|
* set. Connect the ports of the instantiated module to the signals of
|
|
|
|
|
* the parameters. This is done with BUFZ gates so that they look just
|
|
|
|
|
* like continuous assignment connections.
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2000-02-18 06:15:02 +01:00
|
|
|
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(scope);
|
|
|
|
|
|
2005-02-10 05:56:58 +01:00
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Instantiate module "
|
2005-11-10 14:28:11 +01:00
|
|
|
<< rmod->mod_name() << " with instance name "
|
2007-06-02 05:42:12 +02:00
|
|
|
<< get_name() << " in scope " << scope_path(scope) << endl;
|
2005-02-10 05:56:58 +01:00
|
|
|
}
|
|
|
|
|
|
2004-03-08 01:47:44 +01:00
|
|
|
// This is the array of pin expressions, shuffled to match the
|
|
|
|
|
// order of the declaration. If the source instantiation uses
|
2008-11-16 22:27:56 +01:00
|
|
|
// bind by order, this is the same as the source list. Otherwise,
|
2005-02-10 05:56:58 +01:00
|
|
|
// the source list is rearranged by name binding into this list.
|
2010-10-26 04:36:44 +02:00
|
|
|
vector<PExpr*>pins (rmod->port_count());
|
2009-11-26 18:39:21 +01:00
|
|
|
vector<bool>pins_fromwc (rmod->port_count(), false);
|
1999-05-29 04:36:17 +02:00
|
|
|
|
2005-02-10 05:56:58 +01:00
|
|
|
// If the instance has a pins_ member, then we know we are
|
|
|
|
|
// binding by name. Therefore, make up a pins array that
|
|
|
|
|
// reflects the positions of the named ports.
|
1999-05-29 04:36:17 +02:00
|
|
|
if (pins_) {
|
1999-08-03 06:14:49 +02:00
|
|
|
unsigned nexp = rmod->port_count();
|
1999-05-29 04:36:17 +02:00
|
|
|
|
|
|
|
|
// Scan the bindings, matching them with port names.
|
|
|
|
|
for (unsigned idx = 0 ; idx < npins_ ; idx += 1) {
|
|
|
|
|
|
2009-11-26 18:39:21 +01:00
|
|
|
// Handle wildcard named port
|
|
|
|
|
if (pins_[idx].name[0] == '*') {
|
|
|
|
|
for (unsigned j = 0 ; j < nexp ; j += 1) {
|
|
|
|
|
if (!pins[j]) {
|
|
|
|
|
pins_fromwc[j] = true;
|
|
|
|
|
NetNet* net = 0;
|
|
|
|
|
const NetExpr*par = 0;
|
|
|
|
|
NetEvent* eve = 0;
|
|
|
|
|
pform_name_t path_;
|
|
|
|
|
path_.push_back(name_component_t(rmod->ports[j]->name));
|
|
|
|
|
symbol_search(this, des, scope,
|
|
|
|
|
path_, net, par, eve);
|
|
|
|
|
if (net != 0) {
|
|
|
|
|
pins[j] = new PEIdent(rmod->ports[j]->name, true);
|
|
|
|
|
pins[j]->set_lineno(get_lineno());
|
|
|
|
|
pins[j]->set_file(get_file());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
1999-05-29 04:36:17 +02:00
|
|
|
// Given a binding, look at the module port names
|
|
|
|
|
// for the position that matches the binding name.
|
1999-08-03 06:14:49 +02:00
|
|
|
unsigned pidx = rmod->find_port(pins_[idx].name);
|
1999-05-29 04:36:17 +02:00
|
|
|
|
1999-08-03 06:14:49 +02:00
|
|
|
// If the port name doesn't exist, the find_port
|
|
|
|
|
// method will return the port count. Detect that
|
|
|
|
|
// as an error.
|
1999-05-29 04:36:17 +02:00
|
|
|
if (pidx == nexp) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: port ``" <<
|
1999-05-29 04:36:17 +02:00
|
|
|
pins_[idx].name << "'' is not a port of "
|
|
|
|
|
<< get_name() << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-26 18:39:21 +01:00
|
|
|
// If I am overriding a wildcard port, delete and
|
|
|
|
|
// override it
|
|
|
|
|
if (pins_fromwc[pidx]) {
|
|
|
|
|
delete pins[pidx];
|
|
|
|
|
pins_fromwc[pidx] = false;
|
|
|
|
|
|
2014-01-31 01:16:19 +01:00
|
|
|
// If I already explicitly bound something to
|
2009-11-26 18:39:21 +01:00
|
|
|
// this port, then the pins array will already
|
|
|
|
|
// have a pointer value where I want to place this
|
|
|
|
|
// expression.
|
|
|
|
|
} else if (pins[pidx]) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: port ``" <<
|
1999-05-29 04:36:17 +02:00
|
|
|
pins_[idx].name << "'' already bound." <<
|
|
|
|
|
endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-03 06:14:49 +02:00
|
|
|
// OK, do the binding by placing the expression in
|
1999-05-29 04:36:17 +02:00
|
|
|
// the right place.
|
2004-03-08 01:47:44 +01:00
|
|
|
pins[pidx] = pins_[idx].parm;
|
1999-05-29 04:36:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-08-01 07:17:31 +02:00
|
|
|
} else if (pin_count() == 0) {
|
|
|
|
|
|
|
|
|
|
/* Handle the special case that no ports are
|
|
|
|
|
connected. It is possible that this is an empty
|
2003-01-27 06:09:17 +01:00
|
|
|
connect-by-name list, so we'll allow it and assume
|
2001-08-01 07:17:31 +02:00
|
|
|
that is the case. */
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < rmod->port_count() ; idx += 1)
|
2004-03-08 01:47:44 +01:00
|
|
|
pins[idx] = 0;
|
2001-08-01 07:17:31 +02:00
|
|
|
|
1999-05-29 04:36:17 +02:00
|
|
|
} else {
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
/* Otherwise, this is a positional list of port
|
2001-08-01 07:17:31 +02:00
|
|
|
connections. In this case, the port count must be
|
|
|
|
|
right. Check that is is, the get the pin list. */
|
|
|
|
|
|
1999-08-03 06:14:49 +02:00
|
|
|
if (pin_count() != rmod->port_count()) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Wrong number "
|
2001-08-01 07:17:31 +02:00
|
|
|
"of ports. Expecting " << rmod->port_count() <<
|
1999-05-29 04:36:17 +02:00
|
|
|
", got " << pin_count() << "."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No named bindings, just use the positional list I
|
|
|
|
|
// already have.
|
1999-08-03 06:14:49 +02:00
|
|
|
assert(pin_count() == rmod->port_count());
|
1999-05-29 04:36:17 +02:00
|
|
|
pins = get_pins();
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-05 19:44:41 +02:00
|
|
|
// Elaborate these instances of the module. The recursive
|
1998-11-04 00:28:49 +01:00
|
|
|
// elaboration causes the module to generate a netlist with
|
|
|
|
|
// the ports represented by NetNet objects. I will find them
|
|
|
|
|
// later.
|
2004-09-05 19:44:41 +02:00
|
|
|
|
|
|
|
|
NetScope::scope_vec_t&instance = scope->instance_arrays[get_name()];
|
2008-11-16 22:27:56 +01:00
|
|
|
if (debug_elaborate) cerr << get_fileline() << ": debug: start "
|
|
|
|
|
"recursive elaboration of " << instance.size() << " instance(s) of " <<
|
|
|
|
|
get_name() << "..." << endl;
|
2008-11-03 05:08:38 +01:00
|
|
|
for (unsigned inst = 0 ; inst < instance.size() ; inst += 1) {
|
2004-09-05 19:44:41 +02:00
|
|
|
rmod->elaborate(des, instance[inst]);
|
2012-06-04 21:43:33 +02:00
|
|
|
instance[inst]->set_num_ports( rmod->port_count() );
|
2004-09-05 19:44:41 +02:00
|
|
|
}
|
2008-11-16 22:27:56 +01:00
|
|
|
if (debug_elaborate) cerr << get_fileline() << ": debug: ...done." << endl;
|
2004-09-05 19:44:41 +02:00
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
|
|
|
|
|
// Now connect the ports of the newly elaborated designs to
|
1999-05-29 04:36:17 +02:00
|
|
|
// the expressions that are the instantiation parameters. Scan
|
|
|
|
|
// the pins, elaborate the expressions attached to them, and
|
|
|
|
|
// bind them to the port of the elaborated module.
|
1998-11-04 00:28:49 +01:00
|
|
|
|
2000-11-05 07:05:59 +01:00
|
|
|
// This can get rather complicated because the port can be
|
2003-01-27 06:09:17 +01:00
|
|
|
// unconnected (meaning an empty parameter is passed) connected
|
2000-11-05 07:05:59 +01:00
|
|
|
// to a concatenation, or connected to an internally
|
|
|
|
|
// unconnected port.
|
|
|
|
|
|
2010-10-26 04:36:44 +02:00
|
|
|
for (unsigned idx = 0 ; idx < pins.size() ; idx += 1) {
|
2011-03-21 22:29:20 +01:00
|
|
|
bool unconnected_port = false;
|
2000-11-05 07:05:59 +01:00
|
|
|
|
2015-07-11 00:02:27 +02:00
|
|
|
perm_string port_name = rmod->get_port_name(idx);
|
|
|
|
|
|
2000-11-05 07:05:59 +01:00
|
|
|
// Skip unconnected module ports. This happens when a
|
|
|
|
|
// null parameter is passed in.
|
2003-02-22 05:12:49 +01:00
|
|
|
|
2004-03-08 01:47:44 +01:00
|
|
|
if (pins[idx] == 0) {
|
2009-11-26 18:39:21 +01:00
|
|
|
|
|
|
|
|
if (pins_fromwc[idx]) {
|
2015-07-11 00:02:27 +02:00
|
|
|
cerr << get_fileline() << ": error: Wildcard named "
|
|
|
|
|
"port connection (.*) did not find a matching "
|
|
|
|
|
"identifier for port " << (idx+1) << " ("
|
|
|
|
|
<< port_name << ")." << endl;
|
2009-11-26 18:39:21 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-27 22:41:54 +02:00
|
|
|
// We need this information to support the
|
|
|
|
|
// unconnected_drive directive and for a
|
|
|
|
|
// unconnected input warning when asked for.
|
|
|
|
|
vector<PEIdent*> mport = rmod->get_port(idx);
|
2010-04-14 06:29:15 +02:00
|
|
|
if (mport.empty()) continue;
|
2009-05-27 22:41:54 +02:00
|
|
|
|
|
|
|
|
perm_string pname = peek_tail_name(mport[0]->path());
|
|
|
|
|
|
|
|
|
|
NetNet*tmp = instance[0]->find_signal(pname);
|
2014-05-23 23:36:28 +02:00
|
|
|
|
|
|
|
|
// Handle the error case where there is no internal
|
|
|
|
|
// signal connected to the port.
|
|
|
|
|
if (!tmp) continue;
|
2009-05-27 22:41:54 +02:00
|
|
|
assert(tmp);
|
|
|
|
|
|
|
|
|
|
if (tmp->port_type() == NetNet::PINPUT) {
|
|
|
|
|
// If we have an unconnected input convert it
|
|
|
|
|
// as needed if an unconnected_drive directive
|
|
|
|
|
// was given. This only works for tri or wire!
|
|
|
|
|
switch (rmod->uc_drive) {
|
|
|
|
|
case Module::UCD_PULL0:
|
|
|
|
|
convert_net(des, this, tmp, NetNet::TRI0);
|
|
|
|
|
break;
|
|
|
|
|
case Module::UCD_PULL1:
|
|
|
|
|
convert_net(des, this, tmp, NetNet::TRI1);
|
|
|
|
|
break;
|
|
|
|
|
case Module::UCD_NONE:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2003-02-22 05:12:49 +01:00
|
|
|
|
2015-07-11 00:02:27 +02:00
|
|
|
// Print a warning for an unconnected input.
|
2009-05-27 22:41:54 +02:00
|
|
|
if (warn_portbinding) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": warning: "
|
2003-02-22 05:12:49 +01:00
|
|
|
<< "Instantiating module "
|
|
|
|
|
<< rmod->mod_name()
|
2015-07-11 00:02:27 +02:00
|
|
|
<< " with dangling input port "
|
|
|
|
|
<< (idx+1) << " (" << port_name;
|
2009-05-27 22:41:54 +02:00
|
|
|
switch (rmod->uc_drive) {
|
|
|
|
|
case Module::UCD_PULL0:
|
2015-07-11 00:02:27 +02:00
|
|
|
cerr << ") pulled low." << endl;
|
2009-05-27 22:41:54 +02:00
|
|
|
break;
|
|
|
|
|
case Module::UCD_PULL1:
|
2015-07-11 00:02:27 +02:00
|
|
|
cerr << ") pulled high." << endl;
|
2009-05-27 22:41:54 +02:00
|
|
|
break;
|
|
|
|
|
case Module::UCD_NONE:
|
2015-07-11 00:02:27 +02:00
|
|
|
cerr << ") floating." << endl;
|
2009-05-27 22:41:54 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2003-02-22 05:12:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
2011-03-21 22:29:20 +01:00
|
|
|
unconnected_port = true;
|
2003-02-22 05:12:49 +01:00
|
|
|
}
|
|
|
|
|
|
2012-06-04 21:43:33 +02:00
|
|
|
// Inside the module, the port connects zero or more signals
|
2000-11-05 07:05:59 +01:00
|
|
|
// that were already elaborated. List all those signals
|
2004-09-05 19:44:41 +02:00
|
|
|
// and the NetNet equivalents, for all the instances.
|
2008-11-03 05:08:38 +01:00
|
|
|
vector<PEIdent*> mport = rmod->get_port(idx);
|
|
|
|
|
vector<NetNet*> prts (mport.size() * instance.size());
|
1999-08-04 04:13:02 +02:00
|
|
|
|
2005-02-10 05:56:58 +01:00
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: " << get_name()
|
2015-07-11 00:02:27 +02:00
|
|
|
<< ": Port " << (idx+1) << " (" << port_name
|
|
|
|
|
<< ") has " << prts.size() << " sub-ports." << endl;
|
2005-02-10 05:56:58 +01:00
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
// Count the internal vector bits of the port.
|
|
|
|
|
unsigned prts_vector_width = 0;
|
2000-08-18 06:38:57 +02:00
|
|
|
|
2008-11-03 05:08:38 +01:00
|
|
|
for (unsigned inst = 0 ; inst < instance.size() ; inst += 1) {
|
2008-03-07 05:37:08 +01:00
|
|
|
// Scan the instances from MSB to LSB. The port
|
|
|
|
|
// will be assembled in that order as well.
|
2008-11-03 05:08:38 +01:00
|
|
|
NetScope*inst_scope = instance[instance.size()-inst-1];
|
2004-09-05 19:44:41 +02:00
|
|
|
|
2012-06-04 21:43:33 +02:00
|
|
|
unsigned int prt_vector_width = 0;
|
|
|
|
|
PortType::Enum ptype = PortType::PIMPLICIT;
|
2004-09-05 19:44:41 +02:00
|
|
|
// Scan the module sub-ports for this instance...
|
2014-03-23 04:50:47 +01:00
|
|
|
// (Sub-ports are concatenated ports that form the
|
|
|
|
|
// single port for the instance. This is not a
|
|
|
|
|
// commonly used feature.)
|
2008-11-03 05:08:38 +01:00
|
|
|
for (unsigned ldx = 0 ; ldx < mport.size() ; ldx += 1) {
|
|
|
|
|
unsigned lbase = inst * mport.size();
|
2004-09-05 19:44:41 +02:00
|
|
|
PEIdent*pport = mport[ldx];
|
2014-03-23 04:50:47 +01:00
|
|
|
ivl_assert(*this, pport);
|
2012-06-04 21:43:33 +02:00
|
|
|
NetNet *netnet = pport->elaborate_subport(des, inst_scope);
|
|
|
|
|
prts[lbase + ldx] = netnet;
|
|
|
|
|
if (netnet == 0)
|
2004-09-05 19:44:41 +02:00
|
|
|
continue;
|
|
|
|
|
|
2014-03-23 04:50:47 +01:00
|
|
|
ivl_assert(*this, netnet);
|
|
|
|
|
unsigned port_width = netnet->vector_width() * netnet->pin_count();
|
|
|
|
|
prts_vector_width += port_width;
|
|
|
|
|
prt_vector_width += port_width;
|
2012-06-04 21:43:33 +02:00
|
|
|
ptype = PortType::merged(netnet->port_type(), ptype);
|
2004-09-05 19:44:41 +02:00
|
|
|
}
|
2015-07-11 00:02:27 +02:00
|
|
|
inst_scope->add_module_port_info(idx, port_name, ptype, prt_vector_width );
|
1999-08-04 04:13:02 +02:00
|
|
|
}
|
1998-11-04 00:28:49 +01:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
// If I find that the port is unconnected inside the
|
2000-11-05 07:05:59 +01:00
|
|
|
// module, then there is nothing to connect. Skip the
|
2004-09-05 19:44:41 +02:00
|
|
|
// argument.
|
2011-03-21 22:29:20 +01:00
|
|
|
if ((prts_vector_width == 0) || unconnected_port) {
|
2000-11-05 07:05:59 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-05 19:44:41 +02:00
|
|
|
// We know by design that each instance has the same
|
|
|
|
|
// width port. Therefore, the prts_pin_count must be an
|
|
|
|
|
// even multiple of the instance count.
|
2008-11-03 05:08:38 +01:00
|
|
|
assert(prts_vector_width % instance.size() == 0);
|
2004-09-05 19:44:41 +02:00
|
|
|
|
2011-03-13 14:29:42 +01:00
|
|
|
if (!prts.empty() && (prts[0]->port_type() == NetNet::PINPUT)
|
|
|
|
|
&& prts[0]->pin(0).nexus()->drivers_present()
|
|
|
|
|
&& pins[idx]->is_collapsible_net(des, scope)) {
|
|
|
|
|
prts[0]->port_type(NetNet::PINOUT);
|
|
|
|
|
|
|
|
|
|
cerr << pins[idx]->get_fileline() << ": warning: input port "
|
|
|
|
|
<< prts[0]->name() << " is coerced to inout." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-05 19:44:41 +02:00
|
|
|
// Elaborate the expression that connects to the
|
|
|
|
|
// module[s] port. sig is the thing outside the module
|
|
|
|
|
// that connects to the port.
|
2001-07-19 05:43:15 +02:00
|
|
|
|
2014-03-23 04:50:47 +01:00
|
|
|
NetNet*sig = 0;
|
2010-04-14 06:29:15 +02:00
|
|
|
if (prts.empty() || (prts[0]->port_type() == NetNet::PINPUT)) {
|
2002-11-09 20:20:48 +01:00
|
|
|
|
2014-03-23 04:50:47 +01:00
|
|
|
// Special case: If the input port is an unpacked
|
|
|
|
|
// array, then there should be no sub-ports and
|
|
|
|
|
// the r-value expression is processed
|
|
|
|
|
// differently.
|
|
|
|
|
if (prts.size() >= 1 && prts[0]->pin_count()>1) {
|
|
|
|
|
ivl_assert(*this, prts.size()==1);
|
|
|
|
|
|
|
|
|
|
PEIdent*rval_pident = dynamic_cast<PEIdent*> (pins[idx]);
|
|
|
|
|
ivl_assert(*this, rval_pident);
|
|
|
|
|
|
|
|
|
|
NetNet*rval_net = rval_pident->elaborate_unpacked_net(des, scope);
|
|
|
|
|
ivl_assert(*this, rval_net->pin_count() == prts[0]->pin_count());
|
|
|
|
|
assign_unpacked_with_bufz(des, scope, this, prts[0], rval_net);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
/* Input to module. elaborate the expression to
|
|
|
|
|
the desired width. If this in an instance
|
2008-06-24 17:46:16 +02:00
|
|
|
array, then let the net determine its own
|
2005-08-06 19:58:16 +02:00
|
|
|
width. We use that, then, to decide how to hook
|
|
|
|
|
it up.
|
|
|
|
|
|
2008-11-27 00:37:38 +01:00
|
|
|
NOTE that this also handles the case that the
|
2005-08-06 19:58:16 +02:00
|
|
|
port is actually empty on the inside. We assume
|
|
|
|
|
in that case that the port is input. */
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
NetExpr*tmp_expr = elab_and_eval(des, scope, pins[idx], -1);
|
2008-09-09 04:13:49 +02:00
|
|
|
if (tmp_expr == 0) {
|
|
|
|
|
cerr << pins[idx]->get_fileline()
|
2015-07-11 00:02:27 +02:00
|
|
|
<< ": error: Failed to elaborate port expression."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
2008-09-09 04:13:49 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2012-09-30 00:13:45 +02:00
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Elaborating INPUT port expression: " << *tmp_expr << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-22 22:48:34 +01:00
|
|
|
sig = tmp_expr->synthesize(des, scope, tmp_expr);
|
2005-08-06 19:58:16 +02:00
|
|
|
if (sig == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << pins[idx]->get_fileline()
|
2005-08-06 19:58:16 +02:00
|
|
|
<< ": internal error: Port expression "
|
|
|
|
|
<< "too complicated for elaboration." << endl;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-09 04:13:49 +02:00
|
|
|
delete tmp_expr;
|
2008-11-19 02:17:19 +01:00
|
|
|
if (!sig->get_lineno()) sig->set_line(*this);
|
2008-09-09 04:13:49 +02:00
|
|
|
|
2008-08-07 06:04:52 +02:00
|
|
|
if (need_bufz_for_input_port(prts)) {
|
|
|
|
|
NetBUFZ*tmp = new NetBUFZ(scope, scope->local_symbol(),
|
2010-07-12 02:16:15 +02:00
|
|
|
sig->vector_width(), true);
|
2013-04-19 02:35:12 +02:00
|
|
|
tmp->set_line(*this);
|
2008-08-07 06:04:52 +02:00
|
|
|
des->add_node(tmp);
|
|
|
|
|
connect(tmp->pin(1), sig->pin(0));
|
|
|
|
|
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*tmp2_vec = new netvector_t(sig->data_type(),
|
|
|
|
|
sig->vector_width()-1,0);
|
2008-08-07 06:04:52 +02:00
|
|
|
NetNet*tmp2 = new NetNet(scope, scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::WIRE, tmp2_vec);
|
2008-08-07 06:04:52 +02:00
|
|
|
tmp2->local_flag(true);
|
|
|
|
|
tmp2->set_line(*this);
|
|
|
|
|
connect(tmp->pin(0), tmp2->pin(0));
|
|
|
|
|
sig = tmp2;
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-25 03:28:28 +01:00
|
|
|
// If we have a real signal driving a bit/vector port
|
|
|
|
|
// then we convert the real value using the appropriate
|
|
|
|
|
// width cast. Since a real is only one bit the whole
|
|
|
|
|
// thing needs to go to each instance when arrayed.
|
|
|
|
|
if ((sig->data_type() == IVL_VT_REAL ) &&
|
2010-04-14 06:29:15 +02:00
|
|
|
!prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) {
|
2010-10-16 19:53:20 +02:00
|
|
|
sig = cast_to_int4(des, scope, sig,
|
2010-03-25 03:28:28 +01:00
|
|
|
prts_vector_width/instance.size());
|
|
|
|
|
}
|
|
|
|
|
// If we have a bit/vector signal driving a real port
|
|
|
|
|
// then we convert the value to a real.
|
|
|
|
|
if ((sig->data_type() != IVL_VT_REAL ) &&
|
2010-04-14 06:29:15 +02:00
|
|
|
!prts.empty() && (prts[0]->data_type() == IVL_VT_REAL )) {
|
2010-03-25 03:28:28 +01:00
|
|
|
sig = cast_to_real(des, scope, sig);
|
|
|
|
|
}
|
2013-02-25 23:13:05 +01:00
|
|
|
// If we have a 4-state bit/vector signal driving a
|
|
|
|
|
// 2-state port then we convert the value to 2-state.
|
|
|
|
|
if ((sig->data_type() == IVL_VT_LOGIC ) &&
|
|
|
|
|
!prts.empty() && (prts[0]->data_type() == IVL_VT_BOOL )) {
|
|
|
|
|
sig = cast_to_int2(des, scope, sig,
|
|
|
|
|
sig->vector_width());
|
|
|
|
|
}
|
2010-03-25 03:28:28 +01:00
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
} else if (prts[0]->port_type() == NetNet::PINOUT) {
|
|
|
|
|
|
2014-03-23 04:50:47 +01:00
|
|
|
// For now, do not support unpacked array outputs.
|
|
|
|
|
ivl_assert(*this, prts[0]->unpacked_dimensions()==0);
|
|
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
/* Inout to/from module. This is a more
|
|
|
|
|
complicated case, where the expression must be
|
|
|
|
|
an lnet, but also an r-value net.
|
|
|
|
|
|
|
|
|
|
Normally, this winds up being the same as if we
|
|
|
|
|
just elaborated as an lnet, as passing a simple
|
|
|
|
|
identifier elaborates to the same NetNet in
|
|
|
|
|
both cases so the extra elaboration has no
|
|
|
|
|
effect. But if the expression passed to the
|
2008-06-12 19:04:29 +02:00
|
|
|
inout port is a part select, a special part
|
|
|
|
|
select must be created that can pass data in
|
2005-08-06 19:58:16 +02:00
|
|
|
both directions.
|
|
|
|
|
|
|
|
|
|
Use the elaborate_bi_net method to handle all
|
|
|
|
|
the possible cases. */
|
|
|
|
|
|
|
|
|
|
sig = pins[idx]->elaborate_bi_net(des, scope);
|
2002-11-09 20:20:48 +01:00
|
|
|
if (sig == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << pins[idx]->get_fileline() << ": error: "
|
2005-08-06 19:58:16 +02:00
|
|
|
<< "Inout port expression must support "
|
2002-11-09 20:20:48 +01:00
|
|
|
<< "continuous assignment." << endl;
|
2015-07-11 00:02:27 +02:00
|
|
|
cerr << pins[idx]->get_fileline() << ": : Port "
|
|
|
|
|
<< (idx+1) << " (" << port_name << ") of "
|
2011-02-25 05:58:15 +01:00
|
|
|
<< rmod->mod_name() << " is connected to "
|
|
|
|
|
<< *pins[idx] << endl;
|
2002-11-09 20:20:48 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-25 23:15:35 +02:00
|
|
|
// We do not support automatic bits to real conversion
|
|
|
|
|
// for inout ports.
|
|
|
|
|
if ((sig->data_type() == IVL_VT_REAL ) &&
|
|
|
|
|
!prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) {
|
|
|
|
|
cerr << pins[idx]->get_fileline() << ": error: "
|
|
|
|
|
<< "Cannot automatically connect bit based "
|
2015-07-11 00:02:27 +02:00
|
|
|
"inout port " << (idx+1) << " (" << port_name
|
|
|
|
|
<< ") of module " << rmod->mod_name()
|
|
|
|
|
<< " to real signal " << sig->name() << "." << endl;
|
2010-04-25 23:15:35 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2010-10-02 20:02:27 +02:00
|
|
|
|
2010-04-25 23:15:35 +02:00
|
|
|
// We do not support real inout ports at all.
|
|
|
|
|
if (!prts.empty() && (prts[0]->data_type() == IVL_VT_REAL )) {
|
|
|
|
|
cerr << pins[idx]->get_fileline() << ": error: "
|
|
|
|
|
<< "No support for connecting real inout ports ("
|
2015-07-11 00:02:27 +02:00
|
|
|
"port " << (idx+1) << " (" << port_name
|
|
|
|
|
<< ") of module " << rmod->mod_name() << ")." << endl;
|
2010-04-25 23:15:35 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
|
2002-11-09 20:20:48 +01:00
|
|
|
} else {
|
2004-09-05 19:44:41 +02:00
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
/* Port type must be OUTPUT here. */
|
2012-02-25 19:19:48 +01:00
|
|
|
ivl_assert(*this, prts[0]->port_type() == NetNet::POUTPUT);
|
2005-08-06 19:58:16 +02:00
|
|
|
|
2014-03-24 00:53:38 +01:00
|
|
|
// Special case: If the output port is an unpacked
|
|
|
|
|
// array, then there should be no sub-ports and
|
2015-06-02 19:40:24 +02:00
|
|
|
// the passed port expression is processed
|
2014-03-24 00:53:38 +01:00
|
|
|
// differently. Note that we are calling it the
|
|
|
|
|
// "r-value" expression, but since this is an
|
|
|
|
|
// output port, we assign to it from the internal object.
|
|
|
|
|
if (prts[0]->pin_count() > 1) {
|
|
|
|
|
ivl_assert(*this, prts.size()==1);
|
|
|
|
|
|
|
|
|
|
PEIdent*rval_pident = dynamic_cast<PEIdent*>(pins[idx]);
|
|
|
|
|
ivl_assert(*this, rval_pident);
|
|
|
|
|
|
|
|
|
|
NetNet*rval_net = rval_pident->elaborate_unpacked_net(des, scope);
|
|
|
|
|
ivl_assert(*this, rval_net->pin_count() == prts[0]->pin_count());
|
|
|
|
|
|
|
|
|
|
assign_unpacked_with_bufz(des, scope, this, rval_net, prts[0]);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// At this point, arrays are handled.
|
|
|
|
|
ivl_assert(*this, prts[0]->unpacked_dimensions()==0);
|
|
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
/* Output from module. Elaborate the port
|
|
|
|
|
expression as the l-value of a continuous
|
|
|
|
|
assignment, as the port will continuous assign
|
|
|
|
|
into the port. */
|
|
|
|
|
|
2008-03-19 04:50:40 +01:00
|
|
|
sig = pins[idx]->elaborate_lnet(des, scope);
|
2002-11-09 20:20:48 +01:00
|
|
|
if (sig == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << pins[idx]->get_fileline() << ": error: "
|
2005-08-06 19:58:16 +02:00
|
|
|
<< "Output port expression must support "
|
|
|
|
|
<< "continuous assignment." << endl;
|
2015-07-11 00:02:27 +02:00
|
|
|
cerr << pins[idx]->get_fileline() << ": : Port "
|
|
|
|
|
<< (idx+1) << " (" << port_name << ") of "
|
2011-02-25 05:58:15 +01:00
|
|
|
<< rmod->mod_name() << " is connected to "
|
|
|
|
|
<< *pins[idx] << endl;
|
2005-08-06 19:58:16 +02:00
|
|
|
des->errors += 1;
|
2002-11-09 20:20:48 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2005-08-06 19:58:16 +02:00
|
|
|
|
2010-04-25 23:15:35 +02:00
|
|
|
// If we have a real port driving a bit/vector signal
|
|
|
|
|
// then we convert the real value using the appropriate
|
|
|
|
|
// width cast. Since a real is only one bit the whole
|
|
|
|
|
// thing needs to go to each instance when arrayed.
|
|
|
|
|
if ((sig->data_type() != IVL_VT_REAL ) &&
|
|
|
|
|
!prts.empty() && (prts[0]->data_type() == IVL_VT_REAL )) {
|
|
|
|
|
if (sig->vector_width() % instance.size() != 0) {
|
|
|
|
|
cerr << pins[idx]->get_fileline() << ": error: "
|
|
|
|
|
"When automatically converting a real "
|
|
|
|
|
"port of an arrayed instance to a bit "
|
|
|
|
|
"signal" << endl;
|
|
|
|
|
cerr << pins[idx]->get_fileline() << ": : "
|
|
|
|
|
"the signal width ("
|
|
|
|
|
<< sig->vector_width() << ") must be an "
|
|
|
|
|
"integer multiple of the instance count ("
|
|
|
|
|
<< instance.size() << ")." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
prts_vector_width = sig->vector_width();
|
2010-05-13 03:53:56 +02:00
|
|
|
for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) {
|
|
|
|
|
prts[pidx]->port_type(NetNet::NOT_A_PORT);
|
2010-10-16 19:53:20 +02:00
|
|
|
prts[pidx] = cast_to_int4(des, scope, prts[pidx],
|
2010-06-02 04:24:09 +02:00
|
|
|
prts_vector_width /
|
|
|
|
|
instance.size());
|
2010-05-13 03:53:56 +02:00
|
|
|
prts[pidx]->port_type(NetNet::POUTPUT);
|
2010-04-25 23:15:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have a bit/vector port driving a single real
|
|
|
|
|
// signal then we convert the value to a real.
|
|
|
|
|
if ((sig->data_type() == IVL_VT_REAL ) &&
|
|
|
|
|
!prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) {
|
|
|
|
|
prts_vector_width -= prts[0]->vector_width() - 1;
|
|
|
|
|
prts[0]->port_type(NetNet::NOT_A_PORT);
|
|
|
|
|
prts[0] = cast_to_real(des, scope, prts[0]);
|
|
|
|
|
prts[0]->port_type(NetNet::POUTPUT);
|
|
|
|
|
// No support for multiple real drivers.
|
|
|
|
|
if (instance.size() != 1) {
|
|
|
|
|
cerr << pins[idx]->get_fileline() << ": error: "
|
|
|
|
|
<< "Cannot connect an arrayed instance of "
|
|
|
|
|
"module " << rmod->mod_name() << " to "
|
|
|
|
|
"real signal " << sig->name() << "."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-25 23:13:05 +01:00
|
|
|
// If we have a 4-state bit/vector port driving a
|
|
|
|
|
// 2-state signal then we convert the value to 2-state.
|
|
|
|
|
if ((sig->data_type() == IVL_VT_BOOL ) &&
|
|
|
|
|
!prts.empty() && (prts[0]->data_type() == IVL_VT_LOGIC )) {
|
|
|
|
|
for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) {
|
|
|
|
|
prts[pidx]->port_type(NetNet::NOT_A_PORT);
|
|
|
|
|
prts[pidx] = cast_to_int2(des, scope, prts[pidx],
|
|
|
|
|
prts[pidx]->vector_width());
|
|
|
|
|
prts[pidx]->port_type(NetNet::POUTPUT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-25 23:15:35 +02:00
|
|
|
// A real to real connection is not allowed for arrayed
|
|
|
|
|
// instances. You cannot have multiple real drivers.
|
|
|
|
|
if ((sig->data_type() == IVL_VT_REAL ) &&
|
|
|
|
|
!prts.empty() && (prts[0]->data_type() == IVL_VT_REAL ) &&
|
|
|
|
|
instance.size() != 1) {
|
|
|
|
|
cerr << pins[idx]->get_fileline() << ": error: "
|
|
|
|
|
<< "An arrayed instance of " << rmod->mod_name()
|
2015-07-11 00:02:27 +02:00
|
|
|
<< " cannot have a real port (port " << (idx+1)
|
|
|
|
|
<< " : " << port_name << ") connected to a "
|
2010-04-25 23:15:35 +02:00
|
|
|
"real signal (" << sig->name() << ")." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-31 05:11:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(sig);
|
|
|
|
|
|
2002-11-09 20:20:48 +01:00
|
|
|
#ifndef NDEBUG
|
2012-11-13 03:13:41 +01:00
|
|
|
if ((! prts.empty())
|
2001-07-19 05:43:15 +02:00
|
|
|
&& (prts[0]->port_type() != NetNet::PINPUT)) {
|
2002-11-09 20:20:48 +01:00
|
|
|
assert(sig->type() != NetNet::REG);
|
2001-07-19 05:43:15 +02:00
|
|
|
}
|
2002-11-09 20:20:48 +01:00
|
|
|
#endif
|
2000-11-05 07:05:59 +01:00
|
|
|
|
2004-09-05 19:44:41 +02:00
|
|
|
/* If we are working with an instance array, then the
|
|
|
|
|
signal width must match the port width exactly. */
|
2008-11-03 05:08:38 +01:00
|
|
|
if ((instance.size() != 1)
|
2005-01-09 21:16:00 +01:00
|
|
|
&& (sig->vector_width() != prts_vector_width)
|
2008-11-03 05:08:38 +01:00
|
|
|
&& (sig->vector_width() != prts_vector_width/instance.size())) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << pins[idx]->get_fileline() << ": error: "
|
2005-01-09 21:16:00 +01:00
|
|
|
<< "Port expression width " << sig->vector_width()
|
|
|
|
|
<< " does not match expected width "<< prts_vector_width
|
2008-11-03 05:08:38 +01:00
|
|
|
<< " or " << (prts_vector_width/instance.size())
|
2004-09-05 19:44:41 +02:00
|
|
|
<< "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-10 05:56:58 +01:00
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: " << get_name()
|
2015-07-11 00:02:27 +02:00
|
|
|
<< ": Port " << (idx+1) << " (" << port_name
|
|
|
|
|
<< ") has vector width of " << prts_vector_width
|
|
|
|
|
<< "." << endl;
|
2005-02-10 05:56:58 +01:00
|
|
|
}
|
|
|
|
|
|
1999-02-01 01:26:48 +01:00
|
|
|
// Check that the parts have matching pin counts. If
|
2000-10-26 19:09:46 +02:00
|
|
|
// not, they are different widths. Note that idx is 0
|
|
|
|
|
// based, but users count parameter positions from 1.
|
2008-11-03 05:08:38 +01:00
|
|
|
if ((instance.size() == 1)
|
2005-01-09 21:16:00 +01:00
|
|
|
&& (prts_vector_width != sig->vector_width())) {
|
2008-10-15 19:57:37 +02:00
|
|
|
bool as_signed = false;
|
|
|
|
|
|
|
|
|
|
switch (prts[0]->port_type()) {
|
|
|
|
|
case NetNet::POUTPUT:
|
|
|
|
|
as_signed = prts[0]->get_signed();
|
|
|
|
|
break;
|
|
|
|
|
case NetNet::PINPUT:
|
|
|
|
|
as_signed = sig->get_signed();
|
|
|
|
|
break;
|
|
|
|
|
case NetNet::PINOUT:
|
|
|
|
|
/* This may not be correct! */
|
|
|
|
|
as_signed = prts[0]->get_signed() && sig->get_signed();
|
|
|
|
|
break;
|
2012-02-25 19:19:48 +01:00
|
|
|
case NetNet::PREF:
|
|
|
|
|
ivl_assert(*this, 0);
|
|
|
|
|
break;
|
2008-10-15 19:57:37 +02:00
|
|
|
default:
|
|
|
|
|
ivl_assert(*this, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": warning: Port " << (idx+1)
|
2015-07-11 00:02:27 +02:00
|
|
|
<< " (" << port_name << ") of "
|
2005-01-09 21:16:00 +01:00
|
|
|
<< type_ << " expects " << prts_vector_width <<
|
|
|
|
|
" bits, got " << sig->vector_width() << "." << endl;
|
2001-01-09 06:58:47 +01:00
|
|
|
|
2008-10-03 03:38:53 +02:00
|
|
|
// Delete this when inout ports pad correctly.
|
|
|
|
|
if (prts[0]->port_type() == NetNet::PINOUT) {
|
|
|
|
|
if (prts_vector_width > sig->vector_width()) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : Leaving "
|
2005-01-09 21:16:00 +01:00
|
|
|
<< (prts_vector_width-sig->vector_width())
|
2001-01-09 06:58:47 +01:00
|
|
|
<< " high bits of the port unconnected."
|
|
|
|
|
<< endl;
|
2008-10-03 03:38:53 +02:00
|
|
|
} else {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : Leaving "
|
2005-01-09 21:16:00 +01:00
|
|
|
<< (sig->vector_width()-prts_vector_width)
|
2002-08-01 01:55:38 +02:00
|
|
|
<< " high bits of the expression dangling."
|
2001-01-09 06:58:47 +01:00
|
|
|
<< endl;
|
2008-10-03 03:38:53 +02:00
|
|
|
}
|
|
|
|
|
// Keep the if, but delete the "} else" when fixed.
|
|
|
|
|
} else if (prts_vector_width > sig->vector_width()) {
|
2008-10-15 19:57:37 +02:00
|
|
|
cerr << get_fileline() << ": : Padding ";
|
|
|
|
|
if (as_signed) cerr << "(signed) ";
|
|
|
|
|
cerr << (prts_vector_width-sig->vector_width())
|
2008-10-03 03:38:53 +02:00
|
|
|
<< " high bits of the port."
|
|
|
|
|
<< endl;
|
|
|
|
|
} else {
|
2011-03-13 20:01:10 +01:00
|
|
|
if (prts[0]->port_type() == NetNet::PINPUT) {
|
|
|
|
|
cerr << get_fileline() << ": : Pruning ";
|
|
|
|
|
} else {
|
|
|
|
|
cerr << get_fileline() << ": : Padding ";
|
|
|
|
|
}
|
2008-10-15 19:57:37 +02:00
|
|
|
if (as_signed) cerr << "(signed) ";
|
|
|
|
|
cerr << (sig->vector_width()-prts_vector_width)
|
2008-10-03 03:38:53 +02:00
|
|
|
<< " high bits of the expression."
|
|
|
|
|
<< endl;
|
2001-01-09 06:58:47 +01:00
|
|
|
}
|
2007-09-10 06:14:52 +02:00
|
|
|
|
|
|
|
|
sig = resize_net_to_port_(des, scope, sig, prts_vector_width,
|
2008-10-15 19:57:37 +02:00
|
|
|
prts[0]->port_type(), as_signed);
|
1999-02-01 01:26:48 +01:00
|
|
|
}
|
|
|
|
|
|
1999-08-04 04:13:02 +02:00
|
|
|
// Connect the sig expression that is the context of the
|
2001-01-09 06:58:47 +01:00
|
|
|
// module instance to the ports of the elaborated module.
|
|
|
|
|
|
|
|
|
|
// The prts_pin_count variable is the total width of the
|
|
|
|
|
// port and is the maximum number of connections to
|
|
|
|
|
// make. sig is the elaborated expression that connects
|
|
|
|
|
// to that port. If sig has too few pins, then reduce
|
|
|
|
|
// the number of connections to make.
|
|
|
|
|
|
|
|
|
|
// Connect this many of the port pins. If the expression
|
2005-01-09 21:16:00 +01:00
|
|
|
// is too small, then reduce the number of connects.
|
|
|
|
|
unsigned ccount = prts_vector_width;
|
2008-11-03 05:08:38 +01:00
|
|
|
if (instance.size() == 1 && sig->vector_width() < ccount)
|
2005-01-09 21:16:00 +01:00
|
|
|
ccount = sig->vector_width();
|
2001-01-09 06:58:47 +01:00
|
|
|
|
|
|
|
|
// Now scan the concatenation that makes up the port,
|
|
|
|
|
// connecting pins until we run out of port pins or sig
|
2005-01-09 21:16:00 +01:00
|
|
|
// pins. The sig object is the NetNet that is connected
|
|
|
|
|
// to the port from the outside, and the prts object is
|
|
|
|
|
// an array of signals to be connected to the sig.
|
2001-01-09 06:58:47 +01:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
NetConcat*ctmp;
|
|
|
|
|
|
2008-11-03 05:08:38 +01:00
|
|
|
if (prts.size() == 1) {
|
2005-01-09 21:16:00 +01:00
|
|
|
|
|
|
|
|
// The simplest case, there are no
|
|
|
|
|
// parts/concatenations on the inside of the
|
|
|
|
|
// module, so the port and sig need simply be
|
|
|
|
|
// connected directly.
|
|
|
|
|
connect(prts[0]->pin(0), sig->pin(0));
|
|
|
|
|
|
2008-11-03 05:08:38 +01:00
|
|
|
} else if (sig->vector_width()==prts_vector_width/instance.size()
|
|
|
|
|
&& prts.size()/instance.size() == 1) {
|
2005-02-10 05:56:58 +01:00
|
|
|
|
|
|
|
|
if (debug_elaborate){
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: " << get_name()
|
2005-02-10 05:56:58 +01:00
|
|
|
<< ": Replicating " << prts_vector_width
|
|
|
|
|
<< " bits across all "
|
2008-11-03 05:08:38 +01:00
|
|
|
<< prts_vector_width/instance.size()
|
2005-02-10 05:56:58 +01:00
|
|
|
<< " sub-ports." << endl;
|
|
|
|
|
}
|
2005-01-09 21:16:00 +01:00
|
|
|
|
|
|
|
|
// The signal width is exactly the width of a
|
|
|
|
|
// single instance of the port. In this case,
|
|
|
|
|
// connect the sig to all the ports identically.
|
2008-11-03 05:08:38 +01:00
|
|
|
for (unsigned ldx = 0 ; ldx < prts.size() ; ldx += 1)
|
2005-01-09 21:16:00 +01:00
|
|
|
connect(prts[ldx]->pin(0), sig->pin(0));
|
|
|
|
|
|
|
|
|
|
} else switch (prts[0]->port_type()) {
|
|
|
|
|
case NetNet::POUTPUT:
|
|
|
|
|
ctmp = new NetConcat(scope, scope->local_symbol(),
|
2008-11-03 05:08:38 +01:00
|
|
|
prts_vector_width, prts.size());
|
2011-02-10 06:03:08 +01:00
|
|
|
ctmp->set_line(*this);
|
2005-01-09 21:16:00 +01:00
|
|
|
des->add_node(ctmp);
|
|
|
|
|
connect(ctmp->pin(0), sig->pin(0));
|
2008-11-03 05:08:38 +01:00
|
|
|
for (unsigned ldx = 0 ; ldx < prts.size() ; ldx += 1) {
|
2005-01-09 21:16:00 +01:00
|
|
|
connect(ctmp->pin(ldx+1),
|
2008-11-03 05:08:38 +01:00
|
|
|
prts[prts.size()-ldx-1]->pin(0));
|
1999-08-04 04:13:02 +02:00
|
|
|
}
|
2005-01-09 21:16:00 +01:00
|
|
|
break;
|
1998-11-04 00:28:49 +01:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
case NetNet::PINPUT:
|
2005-02-10 05:56:58 +01:00
|
|
|
if (debug_elaborate){
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: " << get_name()
|
2005-02-10 05:56:58 +01:00
|
|
|
<< ": Dividing " << prts_vector_width
|
|
|
|
|
<< " bits across all "
|
2008-11-03 05:08:38 +01:00
|
|
|
<< prts_vector_width/instance.size()
|
2005-02-10 05:56:58 +01:00
|
|
|
<< " input sub-ports of port "
|
2008-11-16 22:27:56 +01:00
|
|
|
<< (idx+1) << "." << endl;
|
2005-02-10 05:56:58 +01:00
|
|
|
}
|
|
|
|
|
|
2011-06-23 19:36:26 +02:00
|
|
|
for (unsigned ldx = 0, spin = 0 ;
|
|
|
|
|
ldx < prts.size() ; ldx += 1) {
|
2008-11-03 05:08:38 +01:00
|
|
|
NetNet*sp = prts[prts.size()-ldx-1];
|
2005-01-09 21:16:00 +01:00
|
|
|
NetPartSelect*ptmp = new NetPartSelect(sig, spin,
|
|
|
|
|
sp->vector_width(),
|
|
|
|
|
NetPartSelect::VP);
|
2011-02-10 06:03:08 +01:00
|
|
|
ptmp->set_line(*this);
|
2005-01-09 21:16:00 +01:00
|
|
|
des->add_node(ptmp);
|
|
|
|
|
connect(ptmp->pin(0), sp->pin(0));
|
|
|
|
|
spin += sp->vector_width();
|
|
|
|
|
}
|
|
|
|
|
break;
|
2008-11-15 00:20:01 +01:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
case NetNet::PINOUT:
|
2011-06-23 19:36:26 +02:00
|
|
|
for (unsigned ldx = 0, spin = 0 ;
|
|
|
|
|
ldx < prts.size() ; ldx += 1) {
|
2008-11-15 00:20:01 +01:00
|
|
|
NetNet*sp = prts[prts.size()-ldx-1];
|
|
|
|
|
NetTran*ttmp = new NetTran(scope,
|
|
|
|
|
scope->local_symbol(),
|
|
|
|
|
sig->vector_width(),
|
|
|
|
|
sp->vector_width(),
|
|
|
|
|
spin);
|
|
|
|
|
ttmp->set_line(*this);
|
2011-02-10 06:03:08 +01:00
|
|
|
des->add_node(ttmp);
|
2008-11-15 00:20:01 +01:00
|
|
|
connect(ttmp->pin(0), sig->pin(0));
|
|
|
|
|
connect(ttmp->pin(1), sp->pin(0));
|
|
|
|
|
spin += sp->vector_width();
|
|
|
|
|
}
|
2005-01-09 21:16:00 +01:00
|
|
|
break;
|
2008-11-15 00:20:01 +01:00
|
|
|
|
2012-02-25 19:19:48 +01:00
|
|
|
case NetNet::PREF:
|
|
|
|
|
cerr << get_fileline() << ": sorry: "
|
|
|
|
|
<< "Reference ports not supported yet." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
break;
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
case NetNet::PIMPLICIT:
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
2005-01-09 21:16:00 +01:00
|
|
|
<< "Unexpected IMPLICIT port" << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
break;
|
|
|
|
|
case NetNet::NOT_A_PORT:
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
2005-01-09 21:16:00 +01:00
|
|
|
<< "Unexpected NOT_A_PORT port." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2001-01-09 06:58:47 +01:00
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
2004-09-05 19:44:41 +02:00
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2011-04-04 19:43:58 +02:00
|
|
|
unsigned PGModule::calculate_instance_count_(Design*des, NetScope*scope,
|
|
|
|
|
long&high, long&low,
|
|
|
|
|
perm_string name) const
|
|
|
|
|
{
|
|
|
|
|
unsigned count = 1;
|
|
|
|
|
high = 0;
|
|
|
|
|
low = 0;
|
|
|
|
|
|
|
|
|
|
/* If the Verilog source has a range specification for the UDP, then
|
|
|
|
|
* I am expected to make more than one gate. Figure out how many are
|
|
|
|
|
* desired. */
|
|
|
|
|
if (msb_) {
|
2011-04-07 04:48:41 +02:00
|
|
|
NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1, true);
|
|
|
|
|
NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1, true);
|
2011-04-04 19:43:58 +02:00
|
|
|
|
|
|
|
|
NetEConst*msb_con = dynamic_cast<NetEConst*>(msb_exp);
|
|
|
|
|
NetEConst*lsb_con = dynamic_cast<NetEConst*>(lsb_exp);
|
|
|
|
|
|
|
|
|
|
if (msb_con == 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: Unable to evaluate "
|
|
|
|
|
"expression " << *msb_ << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lsb_con == 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: Unable to evaluate "
|
|
|
|
|
"expression " << *lsb_ << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verinum msb = msb_con->value();
|
|
|
|
|
verinum lsb = lsb_con->value();
|
|
|
|
|
|
|
|
|
|
delete msb_exp;
|
|
|
|
|
delete lsb_exp;
|
|
|
|
|
|
|
|
|
|
if (msb.as_long() > lsb.as_long())
|
|
|
|
|
count = msb.as_long() - lsb.as_long() + 1;
|
|
|
|
|
else
|
|
|
|
|
count = lsb.as_long() - msb.as_long() + 1;
|
|
|
|
|
|
|
|
|
|
low = lsb.as_long();
|
|
|
|
|
high = msb.as_long();
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": debug: PGModule: Make range "
|
|
|
|
|
<< "[" << high << ":" << low << "]" << " of "
|
|
|
|
|
<< count << " UDPs for " << name << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-14 03:01:34 +01:00
|
|
|
/*
|
|
|
|
|
* From a UDP definition in the source, make a NetUDP
|
|
|
|
|
* object. Elaborate the pin expressions as netlists, then connect
|
|
|
|
|
* those networks to the pins.
|
|
|
|
|
*/
|
2001-04-23 01:09:45 +02:00
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const
|
1998-12-01 01:42:13 +01:00
|
|
|
{
|
2006-01-02 06:33:19 +01:00
|
|
|
NetExpr*rise_expr =0, *fall_expr =0, *decay_expr =0;
|
2001-04-29 01:18:08 +02:00
|
|
|
|
2004-02-18 18:11:54 +01:00
|
|
|
perm_string my_name = get_name();
|
|
|
|
|
if (my_name == 0)
|
2003-03-06 01:28:41 +01:00
|
|
|
my_name = scope->local_symbol();
|
2001-04-29 01:18:08 +02:00
|
|
|
|
2001-12-06 05:44:11 +01:00
|
|
|
/* When the parser notices delay expressions in front of a
|
|
|
|
|
module or primitive, it interprets them as parameter
|
|
|
|
|
overrides. Correct that misconception here. */
|
|
|
|
|
if (overrides_) {
|
2010-10-26 04:36:44 +02:00
|
|
|
if (overrides_->size() > 2) {
|
2009-08-04 05:21:50 +02:00
|
|
|
cerr << get_fileline() << ": error: UDPs take at most two "
|
|
|
|
|
"delay arguments." << endl;
|
2006-01-02 06:33:19 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
2009-08-04 05:21:50 +02:00
|
|
|
PDelays tmp_del;
|
|
|
|
|
tmp_del.set_delays(overrides_, false);
|
|
|
|
|
tmp_del.eval_delays(des, scope, rise_expr, fall_expr,
|
|
|
|
|
decay_expr);
|
2006-01-02 06:33:19 +01:00
|
|
|
}
|
2001-12-06 05:44:11 +01:00
|
|
|
}
|
|
|
|
|
|
2011-04-04 19:43:58 +02:00
|
|
|
long low = 0, high = 0;
|
|
|
|
|
unsigned inst_count = calculate_instance_count_(des, scope, high, low,
|
|
|
|
|
my_name);
|
|
|
|
|
if (inst_count == 0) return;
|
|
|
|
|
|
|
|
|
|
if (inst_count != 1) {
|
|
|
|
|
cerr << get_fileline() << ": sorry: UDPs with a range ("
|
|
|
|
|
<< my_name << " [" << high << ":" << low << "]) are "
|
|
|
|
|
<< "not supported." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-08 01:10:29 +01:00
|
|
|
assert(udp);
|
2001-04-24 04:23:58 +02:00
|
|
|
NetUDP*net = new NetUDP(scope, my_name, udp->ports.count(), udp);
|
2011-02-10 06:03:08 +01:00
|
|
|
net->set_line(*this);
|
2006-01-02 06:33:19 +01:00
|
|
|
net->rise_time(rise_expr);
|
|
|
|
|
net->fall_time(fall_expr);
|
|
|
|
|
net->decay_time(decay_expr);
|
1998-12-01 01:42:13 +01:00
|
|
|
|
2012-11-13 03:13:41 +01:00
|
|
|
struct attrib_list_t*attrib_list;
|
2002-05-23 05:08:50 +02:00
|
|
|
unsigned attrib_list_n = 0;
|
|
|
|
|
attrib_list = evaluate_attributes(attributes, attrib_list_n,
|
|
|
|
|
des, scope);
|
|
|
|
|
|
|
|
|
|
for (unsigned adx = 0 ; adx < attrib_list_n ; adx += 1)
|
|
|
|
|
net->attribute(attrib_list[adx].key, attrib_list[adx].val);
|
|
|
|
|
|
|
|
|
|
delete[]attrib_list;
|
|
|
|
|
|
2004-03-08 01:47:44 +01:00
|
|
|
|
|
|
|
|
// This is the array of pin expressions, shuffled to match the
|
|
|
|
|
// order of the declaration. If the source instantiation uses
|
|
|
|
|
// bind by order, this is the same as the source
|
|
|
|
|
// list. Otherwise, the source list is rearranged by name
|
|
|
|
|
// binding into this list.
|
2010-10-26 04:36:44 +02:00
|
|
|
vector<PExpr*>pins;
|
2004-03-08 01:47:44 +01:00
|
|
|
|
|
|
|
|
// Detect binding by name. If I am binding by name, then make
|
|
|
|
|
// up a pins array that reflects the positions of the named
|
|
|
|
|
// ports. If this is simply positional binding in the first
|
|
|
|
|
// place, then get the binding from the base class.
|
|
|
|
|
if (pins_) {
|
|
|
|
|
unsigned nexp = udp->ports.count();
|
2010-10-26 04:36:44 +02:00
|
|
|
pins = vector<PExpr*>(nexp);
|
2004-03-08 01:47:44 +01:00
|
|
|
|
|
|
|
|
// Scan the bindings, matching them with port names.
|
|
|
|
|
for (unsigned idx = 0 ; idx < npins_ ; idx += 1) {
|
|
|
|
|
|
|
|
|
|
// Given a binding, look at the module port names
|
|
|
|
|
// for the position that matches the binding name.
|
|
|
|
|
unsigned pidx = udp->find_port(pins_[idx].name);
|
|
|
|
|
|
|
|
|
|
// If the port name doesn't exist, the find_port
|
|
|
|
|
// method will return the port count. Detect that
|
|
|
|
|
// as an error.
|
|
|
|
|
if (pidx == nexp) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: port ``" <<
|
2004-03-08 01:47:44 +01:00
|
|
|
pins_[idx].name << "'' is not a port of "
|
|
|
|
|
<< get_name() << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If I already bound something to this port, then
|
|
|
|
|
// the (*exp) array will already have a pointer
|
|
|
|
|
// value where I want to place this expression.
|
|
|
|
|
if (pins[pidx]) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: port ``" <<
|
2004-03-08 01:47:44 +01:00
|
|
|
pins_[idx].name << "'' already bound." <<
|
|
|
|
|
endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// OK, do the binding by placing the expression in
|
|
|
|
|
// the right place.
|
|
|
|
|
pins[pidx] = pins_[idx].parm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
/* Otherwise, this is a positional list of port
|
|
|
|
|
connections. In this case, the port count must be
|
|
|
|
|
right. Check that is is, the get the pin list. */
|
|
|
|
|
|
|
|
|
|
if (pin_count() != udp->ports.count()) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Wrong number "
|
2004-03-08 01:47:44 +01:00
|
|
|
"of ports. Expecting " << udp->ports.count() <<
|
|
|
|
|
", got " << pin_count() << "."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No named bindings, just use the positional list I
|
|
|
|
|
// already have.
|
|
|
|
|
assert(pin_count() == udp->ports.count());
|
|
|
|
|
pins = get_pins();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-03-08 01:10:29 +01:00
|
|
|
/* Handle the output port of the primitive special. It is an
|
|
|
|
|
output port (the only output port) so must be passed an
|
|
|
|
|
l-value net. */
|
2004-03-08 01:47:44 +01:00
|
|
|
if (pins[0] == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": warning: output port unconnected."
|
2004-03-08 01:10:29 +01:00
|
|
|
<< endl;
|
|
|
|
|
|
|
|
|
|
} else {
|
2008-03-19 04:50:40 +01:00
|
|
|
NetNet*sig = pins[0]->elaborate_lnet(des, scope);
|
2004-03-08 01:10:29 +01:00
|
|
|
if (sig == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: "
|
2004-03-08 01:10:29 +01:00
|
|
|
<< "Output port expression is not valid." << endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : Output "
|
2004-03-08 01:10:29 +01:00
|
|
|
<< "port of " << udp->name_
|
|
|
|
|
<< " is " << udp->ports[0] << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
|
|
|
|
connect(sig->pin(0), net->pin(0));
|
|
|
|
|
}
|
2011-02-07 04:47:11 +01:00
|
|
|
if (sig->vector_width() != 1) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Output port expression " << *pins[0]
|
|
|
|
|
<< " is too wide (" << sig->vector_width()
|
|
|
|
|
<< ") expected 1." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
2004-03-08 01:10:29 +01:00
|
|
|
}
|
|
|
|
|
|
1998-12-14 03:01:34 +01:00
|
|
|
/* Run through the pins, making netlists for the pin
|
|
|
|
|
expressions and connecting them to the pin in question. All
|
|
|
|
|
of this is independent of the nature of the UDP. */
|
2004-03-08 01:10:29 +01:00
|
|
|
for (unsigned idx = 1 ; idx < net->pin_count() ; idx += 1) {
|
2004-03-08 01:47:44 +01:00
|
|
|
if (pins[idx] == 0)
|
1998-12-02 05:37:13 +01:00
|
|
|
continue;
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
NetExpr*expr_tmp = elab_and_eval(des, scope, pins[idx], 1);
|
2008-09-09 04:13:49 +02:00
|
|
|
if (expr_tmp == 0) {
|
1999-10-07 07:25:33 +02:00
|
|
|
cerr << "internal error: Expression too complicated "
|
2011-02-07 04:47:11 +01:00
|
|
|
"for elaboration:" << *pins[idx] << endl;
|
1998-12-01 01:42:13 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2008-12-22 22:48:34 +01:00
|
|
|
NetNet*sig = expr_tmp->synthesize(des, scope, expr_tmp);
|
2008-09-09 04:13:49 +02:00
|
|
|
ivl_assert(*this, sig);
|
|
|
|
|
sig->set_line(*this);
|
|
|
|
|
|
|
|
|
|
delete expr_tmp;
|
1998-12-01 01:42:13 +01:00
|
|
|
|
|
|
|
|
connect(sig->pin(0), net->pin(idx));
|
2011-02-07 04:47:11 +01:00
|
|
|
if (sig->vector_width() != 1) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Input port expression " << *pins[idx]
|
|
|
|
|
<< " is too wide (" << sig->vector_width()
|
|
|
|
|
<< ") expected 1." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
1998-12-01 01:42:13 +01:00
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
1998-12-14 03:01:34 +01:00
|
|
|
// All done. Add the object to the design.
|
1998-12-01 01:42:13 +01:00
|
|
|
des->add_node(net);
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-02 18:27:38 +02:00
|
|
|
|
|
|
|
|
bool PGModule::elaborate_sig(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
2012-05-10 04:35:11 +02:00
|
|
|
if (bound_type_) {
|
|
|
|
|
return elaborate_sig_mod_(des, scope, bound_type_);
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-02 18:27:38 +02:00
|
|
|
// Look for the module type
|
2004-02-18 18:11:54 +01:00
|
|
|
map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_);
|
2001-10-21 01:02:39 +02:00
|
|
|
if (mod != pform_modules.end())
|
2000-05-02 18:27:38 +02:00
|
|
|
return elaborate_sig_mod_(des, scope, (*mod).second);
|
|
|
|
|
|
2010-11-01 22:37:06 +01:00
|
|
|
// elaborate_sig_udp_ currently always returns true so skip all this
|
|
|
|
|
// for now.
|
|
|
|
|
#if 0
|
2008-03-21 05:44:35 +01:00
|
|
|
map<perm_string,PUdp*>::const_iterator udp = pform_primitives.find(type_);
|
|
|
|
|
if (udp != pform_primitives.end())
|
|
|
|
|
return elaborate_sig_udp_(des, scope, (*udp).second);
|
2010-11-01 22:37:06 +01:00
|
|
|
#endif
|
2008-03-21 05:44:35 +01:00
|
|
|
|
2000-05-02 18:27:38 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
void PGModule::elaborate(Design*des, NetScope*scope) const
|
1998-12-01 01:42:13 +01:00
|
|
|
{
|
2012-05-10 04:35:11 +02:00
|
|
|
if (bound_type_) {
|
|
|
|
|
elaborate_mod_(des, bound_type_, scope);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
1998-12-01 01:42:13 +01:00
|
|
|
// Look for the module type
|
2004-02-18 18:11:54 +01:00
|
|
|
map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_);
|
2001-10-21 01:02:39 +02:00
|
|
|
if (mod != pform_modules.end()) {
|
2001-11-22 07:20:59 +01:00
|
|
|
elaborate_mod_(des, (*mod).second, scope);
|
1998-12-01 01:42:13 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try a primitive type
|
2004-02-18 18:11:54 +01:00
|
|
|
map<perm_string,PUdp*>::const_iterator udp = pform_primitives.find(type_);
|
2001-10-21 01:02:39 +02:00
|
|
|
if (udp != pform_primitives.end()) {
|
2004-03-08 01:10:29 +01:00
|
|
|
assert((*udp).second);
|
2001-11-22 07:20:59 +01:00
|
|
|
elaborate_udp_(des, (*udp).second, scope);
|
1998-12-01 01:42:13 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: Unknown module type: " <<
|
2000-03-08 05:36:53 +01:00
|
|
|
type_ << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PGModule::elaborate_scope(Design*des, NetScope*sc) const
|
|
|
|
|
{
|
2012-05-10 04:35:11 +02:00
|
|
|
// If the module type is known by design, then go right to it.
|
|
|
|
|
if (bound_type_) {
|
|
|
|
|
elaborate_scope_mod_(des, bound_type_, sc);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2000-03-08 05:36:53 +01:00
|
|
|
// Look for the module type
|
2004-02-18 18:11:54 +01:00
|
|
|
map<perm_string,Module*>::const_iterator mod = pform_modules.find(type_);
|
2001-10-21 01:02:39 +02:00
|
|
|
if (mod != pform_modules.end()) {
|
2012-05-10 04:35:11 +02:00
|
|
|
elaborate_scope_mod_(des, mod->second, sc);
|
2000-03-08 05:36:53 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try a primitive type
|
2004-02-18 18:11:54 +01:00
|
|
|
map<perm_string,PUdp*>::const_iterator udp = pform_primitives.find(type_);
|
2001-10-21 01:02:39 +02:00
|
|
|
if (udp != pform_primitives.end())
|
2000-03-08 05:36:53 +01:00
|
|
|
return;
|
|
|
|
|
|
2001-10-21 01:02:39 +02:00
|
|
|
// Not a module or primitive that I know about yet, so try to
|
|
|
|
|
// load a library module file (which parses some new Verilog
|
|
|
|
|
// code) and try again.
|
2001-10-21 02:42:47 +02:00
|
|
|
if (load_module(type_)) {
|
2001-10-21 01:02:39 +02:00
|
|
|
|
|
|
|
|
// Try again to find the module type
|
|
|
|
|
mod = pform_modules.find(type_);
|
|
|
|
|
if (mod != pform_modules.end()) {
|
2012-05-10 04:35:11 +02:00
|
|
|
elaborate_scope_mod_(des, mod->second, sc);
|
2001-10-21 01:02:39 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2000-03-08 05:36:53 +01:00
|
|
|
|
2001-10-21 01:02:39 +02:00
|
|
|
// Try again to find a primitive type
|
|
|
|
|
udp = pform_primitives.find(type_);
|
|
|
|
|
if (udp != pform_primitives.end())
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Not a module or primitive that I know about or can find by
|
|
|
|
|
// any means, so give up.
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Unknown module type: " << type_ << endl;
|
2003-09-25 02:25:14 +02:00
|
|
|
missing_modules[type_] += 1;
|
2000-03-08 05:36:53 +01:00
|
|
|
des->errors += 1;
|
1998-12-01 01:42:13 +01:00
|
|
|
}
|
|
|
|
|
|
1999-09-15 06:17:52 +02:00
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* Statement::elaborate(Design*des, NetScope*) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: elaborate: "
|
2001-11-22 07:20:59 +01:00
|
|
|
"What kind of statement? " << typeid(*this).name() << endl;
|
1998-11-04 00:28:49 +01:00
|
|
|
NetProc*cur = new NetProc;
|
2000-07-26 07:08:07 +02:00
|
|
|
des->errors += 1;
|
1998-11-04 00:28:49 +01:00
|
|
|
return cur;
|
|
|
|
|
}
|
|
|
|
|
|
1999-05-10 02:16:57 +02:00
|
|
|
|
2000-09-09 17:21:26 +02:00
|
|
|
NetAssign_* PAssign_::elaborate_lval(Design*des, NetScope*scope) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2013-07-25 04:36:11 +02:00
|
|
|
// A function called as a task does not have an L-value.
|
|
|
|
|
if (! lval_) {
|
|
|
|
|
// The R-value must be a simple function call.
|
|
|
|
|
assert (dynamic_cast<PECallFunction*>(rval_));
|
|
|
|
|
PExpr::width_mode_t mode = PExpr::SIZED;
|
|
|
|
|
rval_->test_width(des, scope, mode);
|
|
|
|
|
// Create a L-value that matches the function return type.
|
2014-12-18 17:38:34 +01:00
|
|
|
NetNet*tmp;
|
2013-07-25 04:36:11 +02:00
|
|
|
netvector_t*tmp_vec = new netvector_t(rval_->expr_type(),
|
|
|
|
|
rval_->expr_width()-1, 0,
|
|
|
|
|
rval_->has_sign());
|
2014-12-18 17:38:34 +01:00
|
|
|
|
|
|
|
|
if(rval_->expr_type() == IVL_VT_DARRAY) {
|
|
|
|
|
netdarray_t*darray = new netdarray_t(tmp_vec);
|
|
|
|
|
tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, darray);
|
|
|
|
|
} else {
|
|
|
|
|
tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, tmp_vec);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-25 04:36:11 +02:00
|
|
|
tmp->set_file(rval_->get_file());
|
|
|
|
|
tmp->set_lineno(rval_->get_lineno());
|
|
|
|
|
NetAssign_*lv = new NetAssign_(tmp);
|
|
|
|
|
return lv;
|
|
|
|
|
}
|
2014-09-02 18:23:54 +02:00
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PAssign_::elaborate_lval: "
|
|
|
|
|
<< "lval_ = " << *lval_ << endl;
|
|
|
|
|
cerr << get_fileline() << ": PAssign_::elaborate_lval: "
|
|
|
|
|
<< "lval_ expr type = " << typeid(*lval_).name() << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-18 20:21:37 +02:00
|
|
|
return lval_->elaborate_lval(des, scope, false, false);
|
1999-06-14 01:51:16 +02:00
|
|
|
}
|
|
|
|
|
|
2012-10-01 03:03:10 +02:00
|
|
|
NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope,
|
|
|
|
|
ivl_type_t net_type) const
|
|
|
|
|
{
|
|
|
|
|
ivl_assert(*this, rval_);
|
|
|
|
|
|
|
|
|
|
NetExpr*rv = rval_->elaborate_expr(des, scope, net_type, 0);
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, !is_constant_);
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-21 06:47:07 +02:00
|
|
|
NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope,
|
2013-10-10 05:15:10 +02:00
|
|
|
ivl_type_t lv_net_type,
|
|
|
|
|
ivl_variable_type_t lv_type,
|
2016-02-23 17:53:01 +01:00
|
|
|
unsigned lv_width,
|
|
|
|
|
bool force_unsigned) const
|
2008-08-21 06:47:07 +02:00
|
|
|
{
|
|
|
|
|
ivl_assert(*this, rval_);
|
|
|
|
|
|
2013-09-29 23:48:42 +02:00
|
|
|
// Don't have a good value for the lv_net_type argument to
|
|
|
|
|
// elaborate_rval_expr, so punt and pass nil. In the future we
|
|
|
|
|
// should look into fixing calls to this method to pass a
|
|
|
|
|
// net_type instead of the separate lv_width/lv_type values.
|
2013-10-10 05:15:10 +02:00
|
|
|
NetExpr*rv = elaborate_rval_expr(des, scope, lv_net_type, lv_type, lv_width,
|
2016-02-23 17:53:01 +01:00
|
|
|
rval(), is_constant_, force_unsigned);
|
2008-08-21 06:47:07 +02:00
|
|
|
|
2008-10-04 16:30:34 +02:00
|
|
|
if (!is_constant_ || !rv) return rv;
|
|
|
|
|
|
|
|
|
|
if (dynamic_cast<NetEConst*>(rv)) return rv;
|
|
|
|
|
if (dynamic_cast<NetECReal*>(rv)) return rv;
|
|
|
|
|
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
"The RHS expression must be constant." << endl;
|
|
|
|
|
cerr << get_fileline() << " : "
|
|
|
|
|
"This expression violates the rule: " << *rv << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
delete rv;
|
|
|
|
|
return 0;
|
2008-08-21 06:47:07 +02:00
|
|
|
}
|
|
|
|
|
|
2002-04-22 00:31:02 +02:00
|
|
|
/*
|
|
|
|
|
* This function elaborates delay expressions. This is a little
|
|
|
|
|
* different from normal elaboration because the result may need to be
|
|
|
|
|
* scaled.
|
|
|
|
|
*/
|
|
|
|
|
static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope)
|
|
|
|
|
{
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*dex = elab_and_eval(des, scope, expr, -1);
|
2003-02-08 20:49:21 +01:00
|
|
|
|
2009-04-14 03:06:17 +02:00
|
|
|
/* Print a warning if we find default and `timescale based
|
|
|
|
|
* delays in the design, since this is likely an error. */
|
|
|
|
|
if (scope->time_from_timescale()) dly_used_timescale = true;
|
|
|
|
|
else dly_used_no_timescale = true;
|
|
|
|
|
|
|
|
|
|
if (display_ts_dly_warning &&
|
|
|
|
|
dly_used_no_timescale && dly_used_timescale) {
|
|
|
|
|
cerr << "warning: Found both default and "
|
|
|
|
|
"`timescale based delays. Use" << endl;
|
|
|
|
|
cerr << " -Wtimescale to find the "
|
|
|
|
|
"module(s) with no `timescale." << endl;
|
|
|
|
|
display_ts_dly_warning = false;
|
|
|
|
|
}
|
|
|
|
|
|
2003-02-08 20:49:21 +01:00
|
|
|
/* If the delay expression is a real constant or vector
|
|
|
|
|
constant, then evaluate it, scale it to the local time
|
|
|
|
|
units, and return an adjusted NetEConst. */
|
|
|
|
|
|
|
|
|
|
if (NetECReal*tmp = dynamic_cast<NetECReal*>(dex)) {
|
2010-06-19 01:03:17 +02:00
|
|
|
uint64_t delay = get_scaled_time_from_real(des, scope, tmp);
|
2002-04-22 00:31:02 +02:00
|
|
|
|
2003-02-08 20:49:21 +01:00
|
|
|
delete tmp;
|
2010-06-19 01:03:17 +02:00
|
|
|
NetEConst*tmp2 = new NetEConst(verinum(delay, 64));
|
|
|
|
|
tmp2->set_line(*expr);
|
|
|
|
|
return tmp2;
|
2002-04-22 00:31:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-02-08 20:49:21 +01:00
|
|
|
if (NetEConst*tmp = dynamic_cast<NetEConst*>(dex)) {
|
|
|
|
|
verinum fn = tmp->value();
|
2010-06-19 01:03:17 +02:00
|
|
|
uint64_t delay = des->scale_to_precision(fn.as_ulong64(), scope);
|
2003-02-08 20:49:21 +01:00
|
|
|
|
|
|
|
|
delete tmp;
|
2010-06-19 01:03:17 +02:00
|
|
|
NetEConst*tmp2 = new NetEConst(verinum(delay, 64));
|
|
|
|
|
tmp2->set_line(*expr);
|
|
|
|
|
return tmp2;
|
2002-04-22 00:31:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-02-08 20:49:21 +01:00
|
|
|
/* The expression is not constant, so generate an expanded
|
|
|
|
|
expression that includes the necessary scale shifts, and
|
|
|
|
|
return that expression. */
|
2010-06-19 01:03:17 +02:00
|
|
|
ivl_assert(*expr, dex);
|
|
|
|
|
if (dex->expr_type() == IVL_VT_REAL) {
|
|
|
|
|
// Scale the real value.
|
|
|
|
|
int shift = scope->time_unit() - scope->time_precision();
|
|
|
|
|
assert(shift >= 0);
|
|
|
|
|
double round = 1;
|
|
|
|
|
for (int lp = 0; lp < shift; lp += 1) round *= 10.0;
|
|
|
|
|
|
|
|
|
|
NetExpr*scal_val = new NetECReal(verireal(round));
|
|
|
|
|
scal_val->set_line(*expr);
|
2011-02-26 23:59:52 +01:00
|
|
|
dex = new NetEBMult('*', dex, scal_val, 1, true);
|
2010-06-19 01:03:17 +02:00
|
|
|
dex->set_line(*expr);
|
|
|
|
|
|
|
|
|
|
// Cast this part of the expression to an integer.
|
2011-08-07 00:27:39 +02:00
|
|
|
dex = new NetECast('v', dex, 64, false);
|
2010-06-19 01:03:17 +02:00
|
|
|
dex->set_line(*expr);
|
|
|
|
|
|
|
|
|
|
// Now scale the integer value.
|
|
|
|
|
shift = scope->time_precision() - des->get_precision();
|
|
|
|
|
assert(shift >= 0);
|
2006-08-08 07:11:37 +02:00
|
|
|
uint64_t scale = 1;
|
2010-06-19 01:03:17 +02:00
|
|
|
for (int lp = 0; lp < shift; lp += 1) scale *= 10;
|
2002-04-22 00:31:02 +02:00
|
|
|
|
2010-06-19 01:03:17 +02:00
|
|
|
scal_val = new NetEConst(verinum(scale, 64));
|
|
|
|
|
scal_val->set_line(*expr);
|
2011-02-26 23:59:52 +01:00
|
|
|
dex = new NetEBMult('*', dex, scal_val, 64, false);
|
2010-06-19 01:03:17 +02:00
|
|
|
dex->set_line(*expr);
|
|
|
|
|
} else {
|
|
|
|
|
int shift = scope->time_unit() - des->get_precision();
|
|
|
|
|
assert(shift >= 0);
|
|
|
|
|
uint64_t scale = 1;
|
|
|
|
|
for (int lp = 0; lp < shift; lp += 1) scale *= 10;
|
2002-04-22 00:31:02 +02:00
|
|
|
|
2010-06-19 01:03:17 +02:00
|
|
|
NetExpr*scal_val = new NetEConst(verinum(scale, 64));
|
|
|
|
|
scal_val->set_line(*expr);
|
2011-02-26 23:59:52 +01:00
|
|
|
dex = new NetEBMult('*', dex, scal_val, 64, false);
|
2010-06-19 01:03:17 +02:00
|
|
|
dex->set_line(*expr);
|
2002-04-22 00:31:02 +02:00
|
|
|
}
|
|
|
|
|
|
2003-02-08 20:49:21 +01:00
|
|
|
return dex;
|
2002-04-22 00:31:02 +02:00
|
|
|
}
|
|
|
|
|
|
2011-11-27 20:16:39 +01:00
|
|
|
NetProc* PAssign::elaborate_compressed_(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
ivl_assert(*this, ! delay_);
|
|
|
|
|
ivl_assert(*this, ! count_);
|
|
|
|
|
ivl_assert(*this, ! event_);
|
|
|
|
|
|
|
|
|
|
NetAssign_*lv = elaborate_lval(des, scope);
|
|
|
|
|
if (lv == 0) return 0;
|
|
|
|
|
|
2016-02-23 17:53:01 +01:00
|
|
|
// Compressed assignments should behave identically to the
|
|
|
|
|
// equivalent uncompressed assignments. This means we need
|
|
|
|
|
// to take the type of the LHS into account when determining
|
|
|
|
|
// the type of the RHS expression.
|
|
|
|
|
NetExpr*rv = elaborate_rval_(des, scope, 0, lv->expr_type(),
|
|
|
|
|
count_lval_width(lv), !lv->get_signed());
|
2011-11-27 20:16:39 +01:00
|
|
|
if (rv == 0) return 0;
|
|
|
|
|
|
2011-11-28 01:46:02 +01:00
|
|
|
NetAssign*cur = new NetAssign(lv, op_, rv);
|
2011-11-27 20:16:39 +01:00
|
|
|
cur->set_line(*this);
|
|
|
|
|
|
|
|
|
|
return cur;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-03 04:41:58 +02:00
|
|
|
/*
|
|
|
|
|
* Assignments within program blocks can only write to certain types
|
|
|
|
|
* of variables. We can only write to:
|
|
|
|
|
* - variables in a program block
|
|
|
|
|
* - static properties of a class
|
|
|
|
|
*/
|
2012-05-14 02:48:47 +02:00
|
|
|
static bool lval_not_program_variable(const NetAssign_*lv)
|
|
|
|
|
{
|
|
|
|
|
while (lv) {
|
2013-11-17 01:27:05 +01:00
|
|
|
NetScope*sig_scope = lv->scope();
|
2013-07-03 04:41:58 +02:00
|
|
|
if (! sig_scope->program_block() && sig_scope->type()!=NetScope::CLASS)
|
2012-05-14 02:48:47 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
lv = lv->more;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PAssign::elaborate(Design*des, NetScope*scope) const
|
1999-06-14 01:51:16 +02:00
|
|
|
{
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(scope);
|
|
|
|
|
|
2011-11-27 20:16:39 +01:00
|
|
|
/* If this is a compressed assignment, then handle the
|
|
|
|
|
elaboration in a specialized function. */
|
|
|
|
|
if (op_ != 0)
|
|
|
|
|
return elaborate_compressed_(des, scope);
|
|
|
|
|
|
1999-07-12 02:59:36 +02:00
|
|
|
/* elaborate the lval. This detects any part selects and mux
|
|
|
|
|
expressions that might exist. */
|
2000-09-03 19:58:35 +02:00
|
|
|
NetAssign_*lv = elaborate_lval(des, scope);
|
|
|
|
|
if (lv == 0) return 0;
|
1999-06-14 01:51:16 +02:00
|
|
|
|
2012-05-14 02:48:47 +02:00
|
|
|
if (scope->program_block() && lval_not_program_variable(lv)) {
|
|
|
|
|
cerr << get_fileline() << ": error: Blocking assignments to "
|
|
|
|
|
<< "non-program variables are not allowed." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2002-04-22 00:31:02 +02:00
|
|
|
/* If there is an internal delay expression, elaborate it. */
|
|
|
|
|
NetExpr*delay = 0;
|
|
|
|
|
if (delay_ != 0)
|
|
|
|
|
delay = elaborate_delay_expr(delay_, des, scope);
|
1999-07-12 02:59:36 +02:00
|
|
|
|
2012-10-01 03:03:10 +02:00
|
|
|
NetExpr*rv;
|
2012-11-22 20:08:13 +01:00
|
|
|
const ivl_type_s*lv_net_type = lv->net_type();
|
|
|
|
|
|
2013-01-27 21:30:38 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PAssign::elaborate: ";
|
|
|
|
|
if (lv_net_type)
|
|
|
|
|
cerr << "lv_net_type=" << *lv_net_type << endl;
|
|
|
|
|
else
|
|
|
|
|
cerr << "lv_net_type=<nil>" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-22 20:08:13 +01:00
|
|
|
/* If the l-value is a compound type of some sort, then use
|
|
|
|
|
the newer net_type form of the elaborate_rval_ method to
|
|
|
|
|
handle the new types. */
|
|
|
|
|
if (dynamic_cast<const netclass_t*> (lv_net_type)) {
|
|
|
|
|
ivl_assert(*this, lv->more==0);
|
|
|
|
|
rv = elaborate_rval_(des, scope, lv_net_type);
|
|
|
|
|
|
2013-01-27 21:30:38 +01:00
|
|
|
} else if (const netdarray_t*dtype = dynamic_cast<const netdarray_t*> (lv_net_type)) {
|
2012-11-22 20:08:13 +01:00
|
|
|
ivl_assert(*this, lv->more==0);
|
2013-01-27 21:30:38 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
if (lv->word())
|
|
|
|
|
cerr << get_fileline() << ": PAssign::elaborate: "
|
|
|
|
|
<< "lv->word() = " << *lv->word() << endl;
|
|
|
|
|
else
|
|
|
|
|
cerr << get_fileline() << ": PAssign::elaborate: "
|
|
|
|
|
<< "lv->word() = <nil>" << endl;
|
|
|
|
|
}
|
|
|
|
|
ivl_type_t use_lv_type = lv_net_type;
|
|
|
|
|
if (lv->word())
|
|
|
|
|
use_lv_type = dtype->element_type();
|
|
|
|
|
|
|
|
|
|
rv = elaborate_rval_(des, scope, use_lv_type);
|
2012-11-12 02:42:31 +01:00
|
|
|
|
2014-09-02 18:23:54 +02:00
|
|
|
} else if (const netuarray_t*utype = dynamic_cast<const netuarray_t*>(lv_net_type)) {
|
|
|
|
|
ivl_assert(*this, lv->more==0);
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
if (lv->word())
|
|
|
|
|
cerr << get_fileline() << ": PAssign::elaborate: "
|
|
|
|
|
<< "lv->word() = " << *lv->word() << endl;
|
|
|
|
|
else
|
|
|
|
|
cerr << get_fileline() << ": PAssign::elaborate: "
|
|
|
|
|
<< "lv->word() = <nil>" << endl;
|
|
|
|
|
}
|
|
|
|
|
ivl_type_t use_lv_type = lv_net_type;
|
|
|
|
|
ivl_assert(*this, lv->word());
|
|
|
|
|
use_lv_type = utype->element_type();
|
|
|
|
|
|
2014-09-16 02:33:56 +02:00
|
|
|
ivl_assert(*this, use_lv_type);
|
2014-09-02 18:23:54 +02:00
|
|
|
rv = elaborate_rval_(des, scope, use_lv_type);
|
|
|
|
|
|
2012-10-01 03:03:10 +02:00
|
|
|
} else {
|
|
|
|
|
/* Elaborate the r-value expression, then try to evaluate it. */
|
2013-10-10 05:15:10 +02:00
|
|
|
rv = elaborate_rval_(des, scope, lv_net_type, lv->expr_type(), count_lval_width(lv));
|
2012-10-01 03:03:10 +02:00
|
|
|
}
|
1999-06-14 01:51:16 +02:00
|
|
|
|
2004-06-01 01:34:36 +02:00
|
|
|
if (rv == 0) return 0;
|
1999-06-14 01:51:16 +02:00
|
|
|
assert(rv);
|
|
|
|
|
|
2008-09-04 01:05:12 +02:00
|
|
|
if (count_) assert(event_);
|
2000-04-28 18:50:53 +02:00
|
|
|
|
1999-07-12 02:59:36 +02:00
|
|
|
/* Rewrite delayed assignments as assignments that are
|
1999-07-13 06:08:26 +02:00
|
|
|
delayed. For example, a = #<d> b; becomes:
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
tmp = b;
|
|
|
|
|
#<d> a = tmp;
|
|
|
|
|
end
|
|
|
|
|
|
1999-09-22 04:00:48 +02:00
|
|
|
If the delay is an event delay, then the transform is
|
|
|
|
|
similar, with the event delay replacing the time delay. It
|
|
|
|
|
is an event delay if the event_ member has a value.
|
|
|
|
|
|
1999-07-13 06:08:26 +02:00
|
|
|
This rewriting of the expression allows me to not bother to
|
|
|
|
|
actually and literally represent the delayed assign in the
|
1999-08-18 06:00:02 +02:00
|
|
|
netlist. The compound statement is exactly equivalent. */
|
1999-07-13 06:08:26 +02:00
|
|
|
|
2002-04-22 00:31:02 +02:00
|
|
|
if (delay || event_) {
|
2008-05-10 00:25:42 +02:00
|
|
|
unsigned wid = count_lval_width(lv);
|
1999-07-17 20:06:02 +02:00
|
|
|
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*tmp2_vec = new netvector_t(rv->expr_type(),wid-1,0);
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::REG, tmp2_vec);
|
2007-12-28 00:47:01 +01:00
|
|
|
tmp->local_flag(true);
|
1999-07-13 06:08:26 +02:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
|
2000-09-03 19:58:35 +02:00
|
|
|
NetESignal*sig = new NetESignal(tmp);
|
|
|
|
|
|
1999-09-13 05:10:59 +02:00
|
|
|
/* Generate an assignment of the l-value to the temporary... */
|
2001-08-26 01:50:02 +02:00
|
|
|
NetAssign_*lvt = new NetAssign_(tmp);
|
2000-09-03 01:40:12 +02:00
|
|
|
|
2000-09-03 19:58:35 +02:00
|
|
|
NetAssign*a1 = new NetAssign(lvt, rv);
|
2000-09-03 01:40:12 +02:00
|
|
|
a1->set_line(*this);
|
1999-07-12 02:59:36 +02:00
|
|
|
|
1999-09-13 05:10:59 +02:00
|
|
|
/* Generate an assignment of the temporary to the r-value... */
|
2000-09-03 01:40:12 +02:00
|
|
|
NetAssign*a2 = new NetAssign(lv, sig);
|
|
|
|
|
a2->set_line(*this);
|
1999-07-13 06:08:26 +02:00
|
|
|
|
1999-09-22 04:00:48 +02:00
|
|
|
/* Generate the delay statement with the final
|
|
|
|
|
assignment attached to it. If this is an event delay,
|
|
|
|
|
elaborate the PEventStatement. Otherwise, create the
|
2008-09-04 01:05:12 +02:00
|
|
|
right NetPDelay object. For a repeat event control
|
|
|
|
|
repeat the event and then do the final assignment. */
|
1999-09-22 04:00:48 +02:00
|
|
|
NetProc*st;
|
|
|
|
|
if (event_) {
|
2008-09-04 01:05:12 +02:00
|
|
|
if (count_) {
|
|
|
|
|
NetExpr*count = elab_and_eval(des, scope, count_, -1);
|
|
|
|
|
if (count == 0) {
|
|
|
|
|
cerr << get_fileline() << ": Unable to "
|
|
|
|
|
"elaborate repeat expression." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
st = event_->elaborate(des, scope);
|
|
|
|
|
if (st == 0) {
|
|
|
|
|
cerr << event_->get_fileline() << ": error: "
|
|
|
|
|
"unable to elaborate event expression."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-04-19 03:17:16 +02:00
|
|
|
st->set_line(*this);
|
1999-09-22 04:00:48 +02:00
|
|
|
|
2008-09-04 01:05:12 +02:00
|
|
|
// If the expression is a constant, handle
|
|
|
|
|
// certain special iteration counts.
|
|
|
|
|
if (NetEConst*ce = dynamic_cast<NetEConst*>(count)) {
|
|
|
|
|
long val = ce->value().as_long();
|
|
|
|
|
// We only need the real statement.
|
|
|
|
|
if (val <= 0) {
|
|
|
|
|
delete count;
|
|
|
|
|
delete st;
|
|
|
|
|
st = 0;
|
|
|
|
|
|
|
|
|
|
// We don't need the repeat statement.
|
|
|
|
|
} else if (val == 1) {
|
|
|
|
|
delete count;
|
|
|
|
|
|
|
|
|
|
// We need a repeat statement.
|
|
|
|
|
} else {
|
|
|
|
|
st = new NetRepeat(count, st);
|
2011-02-28 04:20:16 +01:00
|
|
|
st->set_line(*this);
|
2008-09-04 01:05:12 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
st = new NetRepeat(count, st);
|
2011-02-28 04:20:16 +01:00
|
|
|
st->set_line(*this);
|
2008-09-04 01:05:12 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
st = event_->elaborate_st(des, scope, a2);
|
|
|
|
|
if (st == 0) {
|
|
|
|
|
cerr << event_->get_fileline() << ": error: "
|
|
|
|
|
"unable to elaborate event expression."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-04-19 03:17:16 +02:00
|
|
|
st->set_line(*this);
|
2008-09-04 01:05:12 +02:00
|
|
|
}
|
1999-09-22 04:00:48 +02:00
|
|
|
} else {
|
2002-04-22 00:31:02 +02:00
|
|
|
NetPDelay*de = new NetPDelay(delay, a2);
|
2004-06-30 17:32:02 +02:00
|
|
|
de->set_line(*this);
|
1999-09-22 04:00:48 +02:00
|
|
|
st = de;
|
|
|
|
|
}
|
1999-07-13 06:08:26 +02:00
|
|
|
|
1999-09-22 04:00:48 +02:00
|
|
|
/* And build up the complex statement. */
|
2002-05-27 02:08:45 +02:00
|
|
|
NetBlock*bl = new NetBlock(NetBlock::SEQU, 0);
|
1999-07-13 06:08:26 +02:00
|
|
|
bl->append(a1);
|
2008-09-04 01:05:12 +02:00
|
|
|
if (st) bl->append(st);
|
|
|
|
|
if (count_) bl->append(a2);
|
2011-02-10 06:03:08 +01:00
|
|
|
bl->set_line(*this);
|
1999-07-12 02:59:36 +02:00
|
|
|
|
1999-07-13 06:08:26 +02:00
|
|
|
return bl;
|
1999-07-12 02:59:36 +02:00
|
|
|
}
|
|
|
|
|
|
2014-11-04 23:55:40 +01:00
|
|
|
if (lv->enumeration() &&
|
|
|
|
|
! lv->enumeration()->matches(rv->enumeration())) {
|
2010-11-03 04:16:42 +01:00
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Enumeration type mismatch in assignment." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
2011-03-03 05:23:02 +01:00
|
|
|
|
2000-09-03 19:58:35 +02:00
|
|
|
NetAssign*cur = new NetAssign(lv, rv);
|
1999-03-15 03:43:32 +01:00
|
|
|
cur->set_line(*this);
|
1998-11-04 00:28:49 +01:00
|
|
|
|
|
|
|
|
return cur;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-14 02:48:47 +02:00
|
|
|
/*
|
|
|
|
|
* Return true if any lvalue parts are in a program block scope.
|
|
|
|
|
*/
|
|
|
|
|
static bool lval_is_program_variable(const NetAssign_*lv)
|
|
|
|
|
{
|
|
|
|
|
while (lv) {
|
|
|
|
|
NetScope*sig_scope = lv->sig()->scope();
|
|
|
|
|
if (sig_scope->program_block())
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
lv = lv->more;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-06 22:45:38 +02:00
|
|
|
/*
|
2004-06-20 17:59:06 +02:00
|
|
|
* Elaborate non-blocking assignments. The statement is of the general
|
|
|
|
|
* form:
|
|
|
|
|
*
|
|
|
|
|
* <lval> <= #<delay> <rval> ;
|
1999-06-06 22:45:38 +02:00
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const
|
1999-06-06 22:45:38 +02:00
|
|
|
{
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(scope);
|
|
|
|
|
|
2007-08-25 03:48:24 +02:00
|
|
|
if (scope->in_func()) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: functions cannot have non "
|
2007-08-25 03:48:24 +02:00
|
|
|
"blocking assignment statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-30 08:42:26 +02:00
|
|
|
if (scope->in_final()) {
|
|
|
|
|
cerr << get_fileline() << ": error: final procedures cannot have "
|
|
|
|
|
"non blocking assignment statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-09 01:26:55 +01:00
|
|
|
if (scope->is_auto() && lval()->has_aa_term(des, scope)) {
|
|
|
|
|
cerr << get_fileline() << ": error: automatically allocated "
|
|
|
|
|
"variables may not be assigned values using non-blocking "
|
|
|
|
|
"assignments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-20 08:08:53 +02:00
|
|
|
/* Elaborate the l-value. */
|
2000-09-03 19:58:35 +02:00
|
|
|
NetAssign_*lv = elaborate_lval(des, scope);
|
|
|
|
|
if (lv == 0) return 0;
|
1999-06-06 22:45:38 +02:00
|
|
|
|
2012-05-14 02:48:47 +02:00
|
|
|
if (scope->program_block() && lval_is_program_variable(lv)) {
|
|
|
|
|
cerr << get_fileline() << ": error: Non-blocking assignments to "
|
|
|
|
|
<< "program variables are not allowed." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
// This is an error, but we can let elaboration continue
|
|
|
|
|
// because it would necessarily trigger other errors.
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-10 05:15:10 +02:00
|
|
|
NetExpr*rv = elaborate_rval_(des, scope, 0, lv->expr_type(), count_lval_width(lv));
|
2008-11-15 00:43:49 +01:00
|
|
|
if (rv == 0) return 0;
|
1999-09-25 04:57:29 +02:00
|
|
|
|
2002-04-22 00:31:02 +02:00
|
|
|
NetExpr*delay = 0;
|
2008-09-11 04:37:11 +02:00
|
|
|
if (delay_ != 0) {
|
|
|
|
|
assert(count_ == 0 && event_ == 0);
|
2002-04-22 00:31:02 +02:00
|
|
|
delay = elaborate_delay_expr(delay_, des, scope);
|
2008-09-11 04:37:11 +02:00
|
|
|
}
|
1999-06-06 22:45:38 +02:00
|
|
|
|
2008-09-11 04:37:11 +02:00
|
|
|
NetExpr*count = 0;
|
|
|
|
|
NetEvWait*event = 0;
|
2008-09-04 01:05:12 +02:00
|
|
|
if (count_ != 0 || event_ != 0) {
|
|
|
|
|
if (count_ != 0) {
|
2008-11-09 01:26:55 +01:00
|
|
|
if (scope->is_auto() && count_->has_aa_term(des, scope)) {
|
|
|
|
|
cerr << get_fileline() << ": error: automatically "
|
|
|
|
|
"allocated variables may not be referenced "
|
|
|
|
|
"in intra-assignment event controls of "
|
|
|
|
|
"non-blocking assignments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-04 01:05:12 +02:00
|
|
|
assert(event_ != 0);
|
|
|
|
|
count = elab_and_eval(des, scope, count_, -1);
|
|
|
|
|
if (count == 0) {
|
|
|
|
|
cerr << get_fileline() << ": Unable to elaborate "
|
|
|
|
|
"repeat expression." << endl;
|
|
|
|
|
des->errors += 1;
|
2008-09-11 04:37:11 +02:00
|
|
|
return 0;
|
2008-09-04 01:05:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-09 01:26:55 +01:00
|
|
|
if (scope->is_auto() && event_->has_aa_term(des, scope)) {
|
|
|
|
|
cerr << get_fileline() << ": error: automatically "
|
|
|
|
|
"allocated variables may not be referenced "
|
|
|
|
|
"in intra-assignment event controls of "
|
|
|
|
|
"non-blocking assignments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-11 04:37:11 +02:00
|
|
|
NetProc*st = event_->elaborate(des, scope);
|
|
|
|
|
if (st == 0) {
|
2008-09-04 01:05:12 +02:00
|
|
|
cerr << get_fileline() << ": unable to elaborate "
|
|
|
|
|
"event expression." << endl;
|
|
|
|
|
des->errors += 1;
|
2008-09-11 04:37:11 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
event = dynamic_cast<NetEvWait*>(st) ;
|
|
|
|
|
assert(event);
|
|
|
|
|
|
|
|
|
|
// Some constant values are special.
|
|
|
|
|
if (NetEConst*ce = dynamic_cast<NetEConst*>(count)) {
|
|
|
|
|
long val = ce->value().as_long();
|
|
|
|
|
// We only need the assignment statement.
|
|
|
|
|
if (val <= 0) {
|
|
|
|
|
delete count;
|
|
|
|
|
delete event;
|
|
|
|
|
count = 0;
|
|
|
|
|
event = 0;
|
|
|
|
|
// We only need the event.
|
|
|
|
|
} else if (val == 1) {
|
|
|
|
|
delete count;
|
|
|
|
|
count = 0;
|
|
|
|
|
}
|
2008-09-04 01:05:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-13 03:01:51 +02:00
|
|
|
/* All done with this node. Mark its line number and check it in. */
|
2008-09-11 04:37:11 +02:00
|
|
|
NetAssignNB*cur = new NetAssignNB(lv, rv, event, count);
|
2002-04-22 00:31:02 +02:00
|
|
|
cur->set_delay(delay);
|
1999-06-06 22:45:38 +02:00
|
|
|
cur->set_line(*this);
|
|
|
|
|
return cur;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1999-01-25 06:45:56 +01:00
|
|
|
/*
|
|
|
|
|
* This is the elaboration method for a begin-end block. Try to
|
|
|
|
|
* elaborate the entire block, even if it fails somewhere. This way I
|
|
|
|
|
* get all the error messages out of it. Then, if I detected a failure
|
|
|
|
|
* then pass the failure up.
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PBlock::elaborate(Design*des, NetScope*scope) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(scope);
|
|
|
|
|
|
2012-05-20 02:34:13 +02:00
|
|
|
NetBlock::Type type;
|
|
|
|
|
switch (bl_type_) {
|
|
|
|
|
case PBlock::BL_SEQ:
|
|
|
|
|
type = NetBlock::SEQU;
|
|
|
|
|
break;
|
|
|
|
|
case PBlock::BL_PAR:
|
|
|
|
|
type = NetBlock::PARA;
|
|
|
|
|
break;
|
|
|
|
|
case PBlock::BL_JOIN_NONE:
|
|
|
|
|
type = NetBlock::PARA_JOIN_NONE;
|
|
|
|
|
break;
|
|
|
|
|
case PBlock::BL_JOIN_ANY:
|
|
|
|
|
type = NetBlock::PARA_JOIN_ANY;
|
|
|
|
|
break;
|
2012-09-24 19:26:58 +02:00
|
|
|
// Added to remove a "type" uninitialized compiler warning.
|
|
|
|
|
// This should never be reached since all the PBlock enumeration
|
|
|
|
|
// cases are handled above.
|
|
|
|
|
default:
|
|
|
|
|
type = NetBlock::SEQU;
|
|
|
|
|
assert(0);
|
2012-05-20 02:34:13 +02:00
|
|
|
}
|
1999-01-25 06:45:56 +01:00
|
|
|
|
2002-05-27 02:08:45 +02:00
|
|
|
NetScope*nscope = 0;
|
2008-02-16 06:20:24 +01:00
|
|
|
if (pscope_name() != 0) {
|
|
|
|
|
nscope = scope->child(hname_t(pscope_name()));
|
2000-03-11 04:25:51 +01:00
|
|
|
if (nscope == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
2007-06-02 05:42:12 +02:00
|
|
|
"unable to find block scope " << scope_path(scope)
|
2008-06-18 06:45:37 +02:00
|
|
|
<< "." << pscope_name() << endl;
|
2000-03-11 04:25:51 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(nscope);
|
1999-12-15 00:42:16 +01:00
|
|
|
|
2008-03-04 05:49:52 +01:00
|
|
|
elaborate_behaviors_(des, nscope);
|
1999-12-15 00:42:16 +01:00
|
|
|
}
|
1999-06-24 06:24:18 +02:00
|
|
|
|
2002-05-27 02:08:45 +02:00
|
|
|
NetBlock*cur = new NetBlock(type, nscope);
|
|
|
|
|
|
|
|
|
|
if (nscope == 0)
|
|
|
|
|
nscope = scope;
|
|
|
|
|
|
1999-06-07 01:07:43 +02:00
|
|
|
// Handle the special case that the block contains only one
|
2002-08-13 07:35:00 +02:00
|
|
|
// statement. There is no need to keep the block node. Also,
|
|
|
|
|
// don't elide named blocks, because they might be referenced
|
|
|
|
|
// elsewhere.
|
2011-09-17 21:10:05 +02:00
|
|
|
if ((list_.size() == 1) && (pscope_name() == 0)) {
|
2001-10-22 04:05:20 +02:00
|
|
|
assert(list_[0]);
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc*tmp = list_[0]->elaborate(des, nscope);
|
1999-06-07 01:07:43 +02:00
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-17 21:10:05 +02:00
|
|
|
for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) {
|
2001-10-22 04:05:20 +02:00
|
|
|
assert(list_[idx]);
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc*tmp = list_[idx]->elaborate(des, nscope);
|
2007-02-01 06:52:24 +01:00
|
|
|
// If the statement fails to elaborate, then simply
|
|
|
|
|
// ignore it. Presumably, the elaborate for the
|
|
|
|
|
// statement already generated an error message and
|
|
|
|
|
// marked the error count in the design so no need to
|
|
|
|
|
// do any of that here.
|
1999-01-25 06:45:56 +01:00
|
|
|
if (tmp == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2000-09-30 00:58:57 +02:00
|
|
|
|
|
|
|
|
// If the result turns out to be a noop, then skip it.
|
|
|
|
|
if (NetBlock*tbl = dynamic_cast<NetBlock*>(tmp))
|
|
|
|
|
if (tbl->proc_first() == 0) {
|
|
|
|
|
delete tbl;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-25 06:45:56 +01:00
|
|
|
cur->append(tmp);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-09 13:24:50 +01:00
|
|
|
// Update flags in parent scope.
|
|
|
|
|
if (!nscope->is_const_func())
|
|
|
|
|
scope->is_const_func(false);
|
|
|
|
|
if (nscope->calls_sys_task())
|
|
|
|
|
scope->calls_sys_task(true);
|
|
|
|
|
|
2011-02-10 06:03:08 +01:00
|
|
|
cur->set_line(*this);
|
1998-11-04 00:28:49 +01:00
|
|
|
return cur;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-07 13:23:45 +02:00
|
|
|
static int test_case_width(Design*des, NetScope*scope, PExpr*pe,
|
|
|
|
|
PExpr::width_mode_t&mode)
|
|
|
|
|
{
|
|
|
|
|
unsigned expr_width = pe->test_width(des, scope, mode);
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << pe->get_fileline() << ": debug: test_width "
|
|
|
|
|
<< "of case expression " << *pe
|
|
|
|
|
<< endl;
|
|
|
|
|
cerr << pe->get_fileline() << ": "
|
|
|
|
|
<< "returns type=" << pe->expr_type()
|
|
|
|
|
<< ", width=" << expr_width
|
|
|
|
|
<< ", signed=" << pe->has_sign()
|
|
|
|
|
<< ", mode=" << PExpr::width_mode_name(mode)
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
return expr_width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NetExpr*elab_and_eval_case(Design*des, NetScope*scope, PExpr*pe,
|
|
|
|
|
bool context_is_real, bool context_unsigned,
|
|
|
|
|
unsigned context_width)
|
|
|
|
|
{
|
|
|
|
|
if (context_unsigned)
|
|
|
|
|
pe->cast_signed(false);
|
|
|
|
|
|
|
|
|
|
unsigned width = context_is_real ? pe->expr_width() : context_width;
|
|
|
|
|
NetExpr*expr = pe->elaborate_expr(des, scope, width, PExpr::NO_FLAGS);
|
|
|
|
|
if (expr == 0) return 0;
|
|
|
|
|
|
|
|
|
|
if (context_is_real)
|
|
|
|
|
expr = cast_to_real(expr);
|
|
|
|
|
|
|
|
|
|
eval_expr(expr, context_width);
|
|
|
|
|
|
|
|
|
|
return expr;
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-15 07:38:39 +02:00
|
|
|
/*
|
|
|
|
|
* Elaborate a case statement.
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PCase::elaborate(Design*des, NetScope*scope) const
|
1999-02-03 05:20:11 +01:00
|
|
|
{
|
2013-04-07 13:23:45 +02:00
|
|
|
ivl_assert(*this, scope);
|
2000-03-08 05:36:53 +01:00
|
|
|
|
2013-04-07 13:23:45 +02:00
|
|
|
/* The type of the case expression and case item expressions is
|
|
|
|
|
determined according to the following rules:
|
|
|
|
|
|
|
|
|
|
- if any of the expressions is real, all the expressions are
|
|
|
|
|
evaluated as real (non-real expressions will be treated as
|
|
|
|
|
self-determined, then converted to real)
|
|
|
|
|
|
|
|
|
|
- otherwise if any of the expressions is unsigned, all the
|
|
|
|
|
expressions are evaluated as unsigned
|
|
|
|
|
|
|
|
|
|
- otherwise all the expressions are evaluated as signed
|
|
|
|
|
|
|
|
|
|
If the type is not real, the bit width is determined by the
|
|
|
|
|
largest self-determined width of any of the expressions. */
|
|
|
|
|
|
|
|
|
|
PExpr::width_mode_t context_mode = PExpr::SIZED;
|
|
|
|
|
unsigned context_width = test_case_width(des, scope, expr_, context_mode);
|
|
|
|
|
bool context_is_real = (expr_->expr_type() == IVL_VT_REAL);
|
|
|
|
|
bool context_unsigned = !expr_->has_sign();
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0; idx < items_->count(); idx += 1) {
|
|
|
|
|
|
|
|
|
|
PCase::Item*cur = (*items_)[idx];
|
|
|
|
|
|
|
|
|
|
for (list<PExpr*>::iterator idx_expr = cur->expr.begin()
|
|
|
|
|
; idx_expr != cur->expr.end() ; ++idx_expr) {
|
|
|
|
|
|
|
|
|
|
PExpr*cur_expr = *idx_expr;
|
|
|
|
|
ivl_assert(*this, cur_expr);
|
|
|
|
|
|
|
|
|
|
PExpr::width_mode_t cur_mode = PExpr::SIZED;
|
|
|
|
|
unsigned cur_width = test_case_width(des, scope, cur_expr,
|
|
|
|
|
cur_mode);
|
|
|
|
|
if (cur_mode > context_mode)
|
|
|
|
|
context_mode = cur_mode;
|
|
|
|
|
if (cur_width > context_width)
|
|
|
|
|
context_width = cur_width;
|
|
|
|
|
if (cur_expr->expr_type() == IVL_VT_REAL)
|
|
|
|
|
context_is_real = true;
|
|
|
|
|
if (!cur_expr->has_sign())
|
|
|
|
|
context_unsigned = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (context_is_real) {
|
|
|
|
|
context_width = 1;
|
|
|
|
|
context_unsigned = false;
|
|
|
|
|
|
2014-11-02 10:11:54 +01:00
|
|
|
} else if (context_mode >= PExpr::LOSSLESS) {
|
2013-04-07 13:23:45 +02:00
|
|
|
|
|
|
|
|
/* Expressions may choose a different size if they are
|
2014-11-02 10:11:54 +01:00
|
|
|
in a lossless context, so we need to run through the
|
2013-04-07 13:23:45 +02:00
|
|
|
process again to get the final expression width. */
|
|
|
|
|
|
|
|
|
|
context_width = test_case_width(des, scope, expr_, context_mode);
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0; idx < items_->count(); idx += 1) {
|
|
|
|
|
|
|
|
|
|
PCase::Item*cur = (*items_)[idx];
|
|
|
|
|
|
|
|
|
|
for (list<PExpr*>::iterator idx_expr = cur->expr.begin()
|
|
|
|
|
; idx_expr != cur->expr.end() ; ++idx_expr) {
|
|
|
|
|
|
|
|
|
|
PExpr*cur_expr = *idx_expr;
|
|
|
|
|
ivl_assert(*this, cur_expr);
|
|
|
|
|
|
|
|
|
|
unsigned cur_width = test_case_width(des, scope, cur_expr,
|
|
|
|
|
context_mode);
|
|
|
|
|
if (cur_width > context_width)
|
|
|
|
|
context_width = cur_width;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (context_width < integer_width)
|
|
|
|
|
context_width += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-07 20:13:30 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": debug: case context is ";
|
|
|
|
|
if (context_is_real) {
|
|
|
|
|
cerr << "real" << endl;
|
|
|
|
|
} else {
|
|
|
|
|
cerr << (context_unsigned ? "unsigned" : "signed")
|
|
|
|
|
<< " vector, width=" << context_width << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-04-07 13:23:45 +02:00
|
|
|
NetExpr*expr = elab_and_eval_case(des, scope, expr_,
|
|
|
|
|
context_is_real,
|
|
|
|
|
context_unsigned,
|
|
|
|
|
context_width);
|
1999-06-10 06:03:52 +02:00
|
|
|
if (expr == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Unable to elaborate this case"
|
1999-06-10 06:03:52 +02:00
|
|
|
" expression." << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-27 06:09:17 +01:00
|
|
|
/* Count the items in the case statement. Note that there may
|
2002-04-24 19:40:48 +02:00
|
|
|
be some cases that have multiple guards. Count each as a
|
|
|
|
|
separate item. */
|
1999-06-15 07:38:39 +02:00
|
|
|
unsigned icount = 0;
|
|
|
|
|
for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) {
|
|
|
|
|
PCase::Item*cur = (*items_)[idx];
|
|
|
|
|
|
2010-10-26 04:36:44 +02:00
|
|
|
if (cur->expr.empty())
|
1999-06-15 07:38:39 +02:00
|
|
|
icount += 1;
|
|
|
|
|
else
|
2010-10-26 04:36:44 +02:00
|
|
|
icount += cur->expr.size();
|
1999-06-15 07:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
1999-09-29 20:36:02 +02:00
|
|
|
NetCase*res = new NetCase(type_, expr, icount);
|
|
|
|
|
res->set_line(*this);
|
1999-02-03 05:20:11 +01:00
|
|
|
|
2002-01-19 21:09:56 +01:00
|
|
|
/* Iterate over all the case items (guard/statement pairs)
|
|
|
|
|
elaborating them. If the guard has no expression, then this
|
2014-11-02 10:11:54 +01:00
|
|
|
is a "default" case. Otherwise, the guard has one or more
|
2002-01-19 21:09:56 +01:00
|
|
|
expressions, and each guard is a case. */
|
1999-06-15 07:38:39 +02:00
|
|
|
unsigned inum = 0;
|
1999-06-06 22:45:38 +02:00
|
|
|
for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) {
|
1999-02-03 05:20:11 +01:00
|
|
|
|
2013-04-07 13:23:45 +02:00
|
|
|
ivl_assert(*this, inum < icount);
|
1999-06-15 07:38:39 +02:00
|
|
|
PCase::Item*cur = (*items_)[idx];
|
1999-02-03 05:20:11 +01:00
|
|
|
|
2010-10-26 04:36:44 +02:00
|
|
|
if (cur->expr.empty()) {
|
1999-06-15 07:38:39 +02:00
|
|
|
/* If there are no expressions, then this is the
|
|
|
|
|
default case. */
|
|
|
|
|
NetProc*st = 0;
|
|
|
|
|
if (cur->stat)
|
2001-11-22 07:20:59 +01:00
|
|
|
st = cur->stat->elaborate(des, scope);
|
1999-06-15 07:38:39 +02:00
|
|
|
|
|
|
|
|
res->set_case(inum, 0, st);
|
|
|
|
|
inum += 1;
|
|
|
|
|
|
2010-10-26 04:36:44 +02:00
|
|
|
} else for (list<PExpr*>::iterator idx_expr = cur->expr.begin()
|
|
|
|
|
; idx_expr != cur->expr.end() ; ++idx_expr) {
|
1999-06-15 07:38:39 +02:00
|
|
|
|
|
|
|
|
/* If there are one or more expressions, then
|
|
|
|
|
iterate over the guard expressions, elaborating
|
|
|
|
|
a separate case for each. (Yes, the statement
|
|
|
|
|
will be elaborated again for each.) */
|
2010-10-26 04:36:44 +02:00
|
|
|
PExpr*cur_expr = *idx_expr;
|
2013-04-07 13:23:45 +02:00
|
|
|
ivl_assert(*this, cur_expr);
|
|
|
|
|
NetExpr*gu = elab_and_eval_case(des, scope, cur_expr,
|
|
|
|
|
context_is_real,
|
|
|
|
|
context_unsigned,
|
|
|
|
|
context_width);
|
2002-01-19 21:09:56 +01:00
|
|
|
|
2013-04-07 13:23:45 +02:00
|
|
|
NetProc*st = 0;
|
1999-06-15 07:38:39 +02:00
|
|
|
if (cur->stat)
|
2001-11-22 07:20:59 +01:00
|
|
|
st = cur->stat->elaborate(des, scope);
|
1999-06-15 07:38:39 +02:00
|
|
|
|
|
|
|
|
res->set_case(inum, gu, st);
|
|
|
|
|
inum += 1;
|
|
|
|
|
}
|
1999-02-03 05:20:11 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-02 13:37:32 +01:00
|
|
|
res->prune();
|
|
|
|
|
|
1999-02-03 05:20:11 +01:00
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-10 04:45:03 +01:00
|
|
|
NetProc* PChainConstructor::elaborate(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
assert(scope);
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PChainConstructor::elaborate: "
|
|
|
|
|
<< "Elaborate constructor chain in scope=" << scope_path(scope) << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The scope is the <class>.new function, so scope->parent()
|
|
|
|
|
// is the class. Use that to get the class type that we are
|
|
|
|
|
// constructing.
|
|
|
|
|
NetScope*scope_class = scope->parent();
|
|
|
|
|
const netclass_t*class_this = scope_class->class_def();
|
|
|
|
|
ivl_assert(*this, class_this);
|
|
|
|
|
|
|
|
|
|
// We also need the super-class.
|
|
|
|
|
const netclass_t*class_super = class_this->get_super();
|
|
|
|
|
if (class_super == 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Class " << class_this->get_name()
|
|
|
|
|
<< " has no parent class for super.new constructor chaining." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
NetBlock*tmp = new NetBlock(NetBlock::SEQU, 0);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Need the "this" variable for the current constructor. We're
|
|
|
|
|
// going to pass this to the chained constructor.
|
|
|
|
|
NetNet*var_this = scope->find_signal(perm_string::literal("@"));
|
|
|
|
|
|
|
|
|
|
// If super.new is an implicit constructor, then there are no
|
2014-01-29 00:50:27 +01:00
|
|
|
// arguments (other than "this" to worry about, so make a
|
2013-11-10 04:45:03 +01:00
|
|
|
// NetEUFunc and there we go.
|
|
|
|
|
if (NetScope*new_scope = class_super->method_from_name(perm_string::literal("new@"))) {
|
|
|
|
|
NetESignal*eres = new NetESignal(var_this);
|
|
|
|
|
vector<NetExpr*> parms(1);
|
|
|
|
|
parms[0] = eres;
|
|
|
|
|
NetEUFunc*tmp = new NetEUFunc(scope, new_scope, eres, parms, true);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
|
|
|
|
|
NetAssign_*lval_this = new NetAssign_(var_this);
|
|
|
|
|
NetAssign*stmt = new NetAssign(lval_this, tmp);
|
|
|
|
|
stmt->set_line(*this);
|
|
|
|
|
return stmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If super.new(...) is a user defined constructor, then call
|
|
|
|
|
// it. This is a bit more complicated because there may be arguments.
|
|
|
|
|
if (NetScope*new_scope = class_super->method_from_name(perm_string::literal("new"))) {
|
|
|
|
|
|
|
|
|
|
int missing_parms = 0;
|
|
|
|
|
NetFuncDef*def = new_scope->func_def();
|
|
|
|
|
ivl_assert(*this, def);
|
|
|
|
|
|
|
|
|
|
NetESignal*eres = new NetESignal(var_this);
|
|
|
|
|
vector<NetExpr*> parms (def->port_count());
|
|
|
|
|
parms[0] = eres;
|
|
|
|
|
|
|
|
|
|
for (size_t idx = 1 ; idx < parms.size() ; idx += 1) {
|
|
|
|
|
if (idx <= parms_.size() && parms_[idx-1]) {
|
|
|
|
|
PExpr*tmp = parms_[idx-1];
|
|
|
|
|
parms[idx] = elaborate_rval_expr(des, scope,
|
|
|
|
|
def->port(idx)->net_type(),
|
|
|
|
|
def->port(idx)->data_type(),
|
|
|
|
|
def->port(idx)->vector_width(),
|
|
|
|
|
tmp, false);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NetExpr*tmp = def->port_defe(idx)) {
|
|
|
|
|
parms[idx] = tmp;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
missing_parms += 1;
|
|
|
|
|
parms[idx] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (missing_parms) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Missing " << missing_parms
|
|
|
|
|
<< " arguments to constructor " << scope_path(new_scope) << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetEUFunc*tmp = new NetEUFunc(scope, new_scope, eres, parms, true);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
|
|
|
|
|
NetAssign_*lval_this = new NetAssign_(var_this);
|
|
|
|
|
NetAssign*stmt = new NetAssign(lval_this, tmp);
|
|
|
|
|
stmt->set_line(*this);
|
|
|
|
|
return stmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There is no constructor at all in the parent, so skip it.
|
|
|
|
|
NetBlock*tmp = new NetBlock(NetBlock::SEQU, 0);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PCondit::elaborate(Design*des, NetScope*scope) const
|
1998-11-07 18:05:05 +01:00
|
|
|
{
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(scope);
|
|
|
|
|
|
2007-06-28 07:05:36 +02:00
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Elaborate condition statement"
|
2007-06-28 07:05:36 +02:00
|
|
|
<< " with conditional: " << *expr_ << endl;
|
|
|
|
|
|
1999-06-03 07:16:25 +02:00
|
|
|
// Elaborate and try to evaluate the conditional expression.
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*expr = elab_and_eval(des, scope, expr_, -1);
|
1999-06-10 06:03:52 +02:00
|
|
|
if (expr == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Unable to elaborate"
|
1999-06-10 06:03:52 +02:00
|
|
|
" condition expression." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
1999-06-03 07:16:25 +02:00
|
|
|
|
|
|
|
|
// If the condition of the conditional statement is constant,
|
|
|
|
|
// then look at the value and elaborate either the if statement
|
|
|
|
|
// or the else statement. I don't need both. If there is no
|
2008-10-21 18:00:48 +02:00
|
|
|
// else_ statement, then use an empty block as a noop.
|
1999-06-03 07:16:25 +02:00
|
|
|
if (NetEConst*ce = dynamic_cast<NetEConst*>(expr)) {
|
|
|
|
|
verinum val = ce->value();
|
2005-06-17 07:06:47 +02:00
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Condition expression "
|
2005-06-17 07:06:47 +02:00
|
|
|
<< "is a constant " << val << "." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-26 08:10:30 +01:00
|
|
|
verinum::V reduced = verinum::V0;
|
|
|
|
|
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
|
|
|
|
|
reduced = reduced | val[idx];
|
|
|
|
|
|
1999-06-03 07:16:25 +02:00
|
|
|
delete expr;
|
2006-11-26 08:10:30 +01:00
|
|
|
if (reduced == verinum::V1)
|
2006-11-27 03:01:07 +01:00
|
|
|
if (if_) {
|
|
|
|
|
return if_->elaborate(des, scope);
|
|
|
|
|
} else {
|
|
|
|
|
NetBlock*tmp = new NetBlock(NetBlock::SEQU, 0);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
1999-06-03 07:16:25 +02:00
|
|
|
else if (else_)
|
2001-11-22 07:20:59 +01:00
|
|
|
return else_->elaborate(des, scope);
|
1999-06-03 07:16:25 +02:00
|
|
|
else
|
2002-05-27 02:08:45 +02:00
|
|
|
return new NetBlock(NetBlock::SEQU, 0);
|
1999-06-03 07:16:25 +02:00
|
|
|
}
|
1999-06-24 06:24:18 +02:00
|
|
|
|
2007-03-22 17:08:14 +01:00
|
|
|
// If the condition expression is more than 1 bits, then
|
1999-09-16 02:33:45 +02:00
|
|
|
// generate a comparison operator to get the result down to
|
|
|
|
|
// one bit. Turn <e> into <e> != 0;
|
|
|
|
|
|
1999-10-05 08:19:46 +02:00
|
|
|
if (expr->expr_width() < 1) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
1999-10-05 08:19:46 +02:00
|
|
|
"incomprehensible expression width (0)." << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-23 06:03:18 +02:00
|
|
|
// Make sure the condition expression evaluates to a condition.
|
|
|
|
|
expr = condition_reduce(expr);
|
1999-06-24 06:24:18 +02:00
|
|
|
|
1999-06-03 07:16:25 +02:00
|
|
|
// Well, I actually need to generate code to handle the
|
|
|
|
|
// conditional, so elaborate.
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc*i = if_? if_->elaborate(des, scope) : 0;
|
|
|
|
|
NetProc*e = else_? else_->elaborate(des, scope) : 0;
|
1998-11-07 18:05:05 +01:00
|
|
|
|
2003-07-02 06:19:16 +02:00
|
|
|
// Detect the special cases that the if or else statements are
|
|
|
|
|
// empty blocks. If this is the case, remove the blocks as
|
|
|
|
|
// null statements.
|
|
|
|
|
if (NetBlock*tmp = dynamic_cast<NetBlock*>(i)) {
|
|
|
|
|
if (tmp->proc_first() == 0) {
|
|
|
|
|
delete i;
|
|
|
|
|
i = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NetBlock*tmp = dynamic_cast<NetBlock*>(e)) {
|
|
|
|
|
if (tmp->proc_first() == 0) {
|
|
|
|
|
delete e;
|
|
|
|
|
e = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-07 18:05:05 +01:00
|
|
|
NetCondit*res = new NetCondit(expr, i, e);
|
2000-02-14 01:11:11 +01:00
|
|
|
res->set_line(*this);
|
1998-11-07 18:05:05 +01:00
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PCallTask::elaborate(Design*des, NetScope*scope) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
if (peek_tail_name(path_)[0] == '$')
|
2001-11-22 07:20:59 +01:00
|
|
|
return elaborate_sys(des, scope);
|
1999-07-03 04:12:51 +02:00
|
|
|
else
|
2001-11-22 07:20:59 +01:00
|
|
|
return elaborate_usr(des, scope);
|
1999-07-03 04:12:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* A call to a system task involves elaborating all the parameters,
|
|
|
|
|
* then passing the list to the NetSTask object.
|
1999-11-10 03:52:24 +01:00
|
|
|
*XXXX
|
2002-08-15 04:11:54 +02:00
|
|
|
* There is a single special case in the call to a system
|
|
|
|
|
* task. Normally, an expression cannot take an unindexed
|
|
|
|
|
* memory. However, it is possible to take a system task parameter a
|
|
|
|
|
* memory if the expression is trivial.
|
1999-07-03 04:12:51 +02:00
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const
|
1999-07-03 04:12:51 +02:00
|
|
|
{
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(scope);
|
|
|
|
|
|
2009-01-29 06:01:49 +01:00
|
|
|
if (path_.size() > 1) {
|
|
|
|
|
cerr << get_fileline() << ": error: Hierarchical system task names"
|
|
|
|
|
<< " make no sense: " << path_ << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-10 18:50:41 +01:00
|
|
|
unsigned parm_count = parms_.size();
|
1999-07-03 04:12:51 +02:00
|
|
|
|
2002-08-15 04:11:54 +02:00
|
|
|
/* Catch the special case that the system task has no
|
|
|
|
|
parameters. The "()" string will be parsed as a single
|
|
|
|
|
empty parameter, when we really mean no parameters at all. */
|
2012-03-10 18:50:41 +01:00
|
|
|
if ((parm_count== 1) && (parms_[0] == 0))
|
2002-08-15 04:11:54 +02:00
|
|
|
parm_count = 0;
|
|
|
|
|
|
2012-07-17 03:15:29 +02:00
|
|
|
vector<NetExpr*>eparms (parm_count);
|
1999-11-10 03:52:24 +01:00
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
|
2002-08-15 04:11:54 +02:00
|
|
|
for (unsigned idx = 0 ; idx < parm_count ; idx += 1) {
|
2012-03-10 18:50:41 +01:00
|
|
|
PExpr*ex = parms_[idx];
|
2008-10-11 05:42:07 +02:00
|
|
|
if (ex != 0) {
|
2011-02-26 23:59:52 +01:00
|
|
|
eparms[idx] = elab_sys_task_arg(des, scope, name, idx, ex);
|
2008-10-11 05:42:07 +02:00
|
|
|
} else {
|
|
|
|
|
eparms[idx] = 0;
|
|
|
|
|
}
|
1999-07-03 04:12:51 +02:00
|
|
|
}
|
|
|
|
|
|
2009-01-29 06:01:49 +01:00
|
|
|
// Special case: Specify blocks are turned off, and this is an
|
|
|
|
|
// $sdf_annotate system task. There will be nothing for $sdf
|
|
|
|
|
// to annotate, and the user is intending to turn the behavior
|
|
|
|
|
// off anyhow, so replace the system task invocation with a no-op.
|
2011-02-26 23:59:52 +01:00
|
|
|
if (gn_specify_blocks_flag == false && name == "$sdf_annotate") {
|
2009-01-29 06:01:49 +01:00
|
|
|
|
2009-01-30 03:31:45 +01:00
|
|
|
cerr << get_fileline() << ": warning: Omitting $sdf_annotate() "
|
|
|
|
|
<< "since specify blocks are being omitted." << endl;
|
2009-01-29 06:01:49 +01:00
|
|
|
NetBlock*noop = new NetBlock(NetBlock::SEQU, scope);
|
|
|
|
|
noop->set_line(*this);
|
|
|
|
|
return noop;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-09 13:24:50 +01:00
|
|
|
scope->calls_sys_task(true);
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
NetSTask*cur = new NetSTask(name, def_sfunc_as_task, eparms);
|
2007-12-22 15:31:24 +01:00
|
|
|
cur->set_line(*this);
|
1999-07-03 04:12:51 +02:00
|
|
|
return cur;
|
|
|
|
|
}
|
|
|
|
|
|
1999-07-24 04:11:19 +02:00
|
|
|
/*
|
|
|
|
|
* A call to a user defined task is different from a call to a system
|
|
|
|
|
* task because a user task in a netlist has no parameters: the
|
|
|
|
|
* assignments are done by the calling thread. For example:
|
|
|
|
|
*
|
|
|
|
|
* task foo;
|
|
|
|
|
* input a;
|
|
|
|
|
* output b;
|
|
|
|
|
* [...]
|
|
|
|
|
* endtask;
|
|
|
|
|
*
|
|
|
|
|
* [...] foo(x, y);
|
|
|
|
|
*
|
|
|
|
|
* is really:
|
|
|
|
|
*
|
|
|
|
|
* task foo;
|
|
|
|
|
* reg a;
|
|
|
|
|
* reg b;
|
|
|
|
|
* [...]
|
|
|
|
|
* endtask;
|
|
|
|
|
*
|
|
|
|
|
* [...]
|
|
|
|
|
* begin
|
|
|
|
|
* a = x;
|
|
|
|
|
* foo;
|
|
|
|
|
* y = b;
|
|
|
|
|
* end
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const
|
1999-07-03 04:12:51 +02:00
|
|
|
{
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(scope);
|
|
|
|
|
|
2013-04-09 03:12:54 +02:00
|
|
|
NetScope*pscope = scope;
|
|
|
|
|
if (package_) {
|
|
|
|
|
pscope = des->find_package(package_->pscope_name());
|
|
|
|
|
ivl_assert(*this, pscope);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetScope*task = des->find_task(pscope, path_);
|
2001-07-29 00:13:11 +02:00
|
|
|
if (task == 0) {
|
2013-02-06 21:36:54 +01:00
|
|
|
// For SystemVerilog this may be a few other things.
|
2012-07-17 03:15:29 +02:00
|
|
|
if (gn_system_verilog()) {
|
2013-02-06 21:36:54 +01:00
|
|
|
NetProc *tmp;
|
|
|
|
|
// This could be a method attached to a signal?
|
|
|
|
|
tmp = elaborate_method_(des, scope);
|
|
|
|
|
if (tmp) return tmp;
|
|
|
|
|
// Or it could be a function call ignoring the return?
|
|
|
|
|
tmp = elaborate_function_(des, scope);
|
2012-07-17 03:15:29 +02:00
|
|
|
if (tmp) return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Enable of unknown task "
|
2001-12-03 05:47:14 +01:00
|
|
|
<< "``" << path_ << "''." << endl;
|
1999-07-03 04:12:51 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2001-07-29 00:13:11 +02:00
|
|
|
assert(task);
|
2001-10-22 04:05:20 +02:00
|
|
|
assert(task->type() == NetScope::TASK);
|
2001-07-29 00:13:11 +02:00
|
|
|
NetTaskDef*def = task->task_def();
|
2001-10-22 04:05:20 +02:00
|
|
|
if (def == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: task " << path_
|
2007-06-02 05:42:12 +02:00
|
|
|
<< " doesn't have a definition in " << scope_path(scope)
|
2001-10-22 04:05:20 +02:00
|
|
|
<< "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
assert(def);
|
2001-07-29 00:13:11 +02:00
|
|
|
|
2014-09-17 01:49:40 +02:00
|
|
|
/* In SystemVerilog a method calling another method in the
|
|
|
|
|
* current class needs to be elaborated as a method with an
|
|
|
|
|
* implicit this added. */
|
|
|
|
|
if (gn_system_verilog() && (path_.size() == 1)) {
|
|
|
|
|
const NetScope *c_scope = scope->get_class_scope();
|
|
|
|
|
if (c_scope && (c_scope == task->get_class_scope())) {
|
|
|
|
|
NetProc *tmp = elaborate_method_(des, scope, true);
|
|
|
|
|
assert(tmp);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-03-10 18:50:41 +01:00
|
|
|
|
2013-09-21 05:38:53 +02:00
|
|
|
unsigned parm_count = def->port_count();
|
1999-07-24 04:11:19 +02:00
|
|
|
|
2008-10-16 20:34:22 +02:00
|
|
|
/* Handle non-automatic tasks with no parameters specially. There is
|
|
|
|
|
no need to make a sequential block to hold the generated code. */
|
2012-03-10 18:50:41 +01:00
|
|
|
if ((parm_count == 0) && !task->is_auto()) {
|
2013-03-19 03:50:56 +01:00
|
|
|
NetUTask*cur = new NetUTask(task);
|
2007-04-15 22:45:40 +02:00
|
|
|
cur->set_line(*this);
|
1999-07-24 04:11:19 +02:00
|
|
|
return cur;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-19 03:50:56 +01:00
|
|
|
return elaborate_build_call_(des, scope, task, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-01 02:56:16 +02:00
|
|
|
/*
|
|
|
|
|
* This private method is called to elaborate built-in methods. The
|
|
|
|
|
* method_name is the detected name of the built-in method, and the
|
|
|
|
|
* sys_task_name is the internal system-task name to use.
|
|
|
|
|
*/
|
|
|
|
|
NetProc* PCallTask::elaborate_sys_task_method_(Design*des, NetScope*scope,
|
|
|
|
|
NetNet*net,
|
|
|
|
|
perm_string method_name,
|
|
|
|
|
const char*sys_task_name) const
|
|
|
|
|
{
|
|
|
|
|
NetESignal*sig = new NetESignal(net);
|
|
|
|
|
sig->set_line(*this);
|
|
|
|
|
|
2014-08-26 03:03:11 +02:00
|
|
|
/* If there is a single NULL argument then ignore it since it is
|
|
|
|
|
* left over from the parser and is not needed by the method. */
|
|
|
|
|
unsigned nparms = parms_.size();
|
|
|
|
|
if ((nparms == 1) && (parms_[0] == 0)) nparms = 0;
|
|
|
|
|
|
|
|
|
|
vector<NetExpr*>argv (1 + nparms);
|
2014-08-01 02:56:16 +02:00
|
|
|
argv[0] = sig;
|
|
|
|
|
|
2014-08-26 03:03:11 +02:00
|
|
|
for (unsigned idx = 0 ; idx < nparms ; idx += 1) {
|
2014-08-01 02:56:16 +02:00
|
|
|
PExpr*ex = parms_[idx];
|
|
|
|
|
if (ex != 0) {
|
|
|
|
|
argv[idx+1] = elab_sys_task_arg(des, scope,
|
|
|
|
|
method_name,
|
|
|
|
|
idx, ex);
|
|
|
|
|
} else {
|
|
|
|
|
argv[idx+1] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetSTask*sys = new NetSTask(sys_task_name, IVL_SFUNC_AS_TASK_IGNORE, argv);
|
|
|
|
|
sys->set_line(*this);
|
|
|
|
|
return sys;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-17 01:49:40 +02:00
|
|
|
NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope,
|
|
|
|
|
bool add_this_flag) const
|
2013-03-19 03:50:56 +01:00
|
|
|
{
|
|
|
|
|
pform_name_t use_path = path_;
|
|
|
|
|
perm_string method_name = peek_tail_name(use_path);
|
|
|
|
|
use_path.pop_back();
|
|
|
|
|
|
|
|
|
|
NetNet *net;
|
|
|
|
|
const NetExpr *par;
|
|
|
|
|
NetEvent *eve;
|
|
|
|
|
const NetExpr *ex1, *ex2;
|
|
|
|
|
|
2014-09-17 01:49:40 +02:00
|
|
|
/* Add the implicit this reference when requested. */
|
|
|
|
|
if (add_this_flag) {
|
|
|
|
|
assert(use_path.empty());
|
|
|
|
|
use_path.push_front(name_component_t(perm_string::literal("@")));
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-19 03:50:56 +01:00
|
|
|
// There is no signal to search for so this cannot be a method.
|
|
|
|
|
if (use_path.empty()) return 0;
|
|
|
|
|
|
2013-09-17 05:59:36 +02:00
|
|
|
// Search for an object using the use_path. This should
|
|
|
|
|
// resolve to a class object. Note that the "this" symbol
|
|
|
|
|
// (internally represented as "@") is handled by there being a
|
|
|
|
|
// "this" object in the instance scope.
|
2013-03-19 03:50:56 +01:00
|
|
|
symbol_search(this, des, scope, use_path,
|
|
|
|
|
net, par, eve, ex1, ex2);
|
|
|
|
|
|
|
|
|
|
if (net == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
// Is this a delete method for dynamic arrays?
|
|
|
|
|
if (net->darray_type() && method_name=="delete") {
|
2014-08-01 02:56:16 +02:00
|
|
|
return elaborate_sys_task_method_(des, scope, net,
|
|
|
|
|
method_name,
|
|
|
|
|
"$ivl_darray_method$delete");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (net->queue_type()) {
|
|
|
|
|
if (method_name=="push_back")
|
|
|
|
|
return elaborate_sys_task_method_(des, scope, net,
|
|
|
|
|
method_name,
|
|
|
|
|
"$ivl_queue_method$push_back");
|
|
|
|
|
if (method_name=="push_front")
|
|
|
|
|
return elaborate_sys_task_method_(des, scope, net,
|
|
|
|
|
method_name,
|
|
|
|
|
"$ivl_queue_method$push_front");
|
2013-03-19 03:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-15 03:03:21 +02:00
|
|
|
if (const netclass_t*class_type = net->class_type()) {
|
2013-03-19 03:50:56 +01:00
|
|
|
NetScope*task = class_type->method_from_name(method_name);
|
|
|
|
|
if (task == 0) {
|
|
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "Can't find task " << method_name
|
|
|
|
|
<< " in class " << class_type->get_name() << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PCallTask::elaborate_method_: "
|
|
|
|
|
<< "Elaborate " << class_type->get_name()
|
|
|
|
|
<< " method " << task->basename() << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetESignal*use_this = new NetESignal(net);
|
|
|
|
|
use_this->set_line(*this);
|
|
|
|
|
|
|
|
|
|
return elaborate_build_call_(des, scope, task, use_this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-19 04:49:25 +02:00
|
|
|
/*
|
|
|
|
|
* If during elaboration we determine (for sure) that we are calling a
|
|
|
|
|
* task (and not just a void function) then this method tests if that
|
|
|
|
|
* task call is allowed in the current context. If so, return true. If
|
|
|
|
|
* not, print and error message and return false;
|
|
|
|
|
*/
|
|
|
|
|
bool PCallTask::test_task_calls_ok_(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
if (scope->in_func()) {
|
|
|
|
|
cerr << get_fileline() << ": error: Functions cannot enable/call "
|
|
|
|
|
"tasks." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scope->in_final()) {
|
|
|
|
|
cerr << get_fileline() << ": error: final procedures cannot "
|
|
|
|
|
"enable/call tasks." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-19 03:50:56 +01:00
|
|
|
NetProc* PCallTask::elaborate_function_(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
NetFuncDef*func = des->find_function(scope, path_);
|
|
|
|
|
// This is not a function, so this task call cannot be a function
|
|
|
|
|
// call with a missing return assignment.
|
|
|
|
|
if (! func) return 0;
|
|
|
|
|
|
2013-07-25 04:36:11 +02:00
|
|
|
// Generate a function call version of this task call.
|
|
|
|
|
PExpr*rval = new PECallFunction(package_, path_, parms_);
|
|
|
|
|
rval->set_file(get_file());
|
|
|
|
|
rval->set_lineno(get_lineno());
|
|
|
|
|
// Generate an assign to nothing.
|
|
|
|
|
PAssign*tmp = new PAssign(0, rval);
|
|
|
|
|
tmp->set_file(get_file());
|
|
|
|
|
tmp->set_lineno(get_lineno());
|
|
|
|
|
cerr << get_fileline() << ": warning: User function '"
|
|
|
|
|
<< peek_tail_name(path_) << "' is being called as a task." << endl;
|
|
|
|
|
// Elaborate the assignment to a dummy variable.
|
|
|
|
|
return tmp->elaborate(des, scope);
|
2013-03-19 03:50:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope,
|
|
|
|
|
NetScope*task, NetExpr*use_this) const
|
|
|
|
|
{
|
2013-09-19 03:48:16 +02:00
|
|
|
NetBaseDef*def = 0;
|
|
|
|
|
if (task->type() == NetScope::TASK) {
|
|
|
|
|
def = task->task_def();
|
|
|
|
|
|
2013-09-19 04:49:25 +02:00
|
|
|
// OK, this is certainly a TASK that I'm calling. Make
|
|
|
|
|
// sure that is OK where I am. Even if this test fails,
|
|
|
|
|
// continue with the elaboration as if it were OK so
|
|
|
|
|
// that we can catch more errors.
|
|
|
|
|
test_task_calls_ok_(des, scope);
|
|
|
|
|
|
2013-09-19 03:48:16 +02:00
|
|
|
} else if (task->type() == NetScope::FUNC) {
|
|
|
|
|
NetFuncDef*tmp = task->func_def();
|
|
|
|
|
if (tmp->return_sig() != 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Calling a non-void function as a task." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
def = tmp;
|
|
|
|
|
}
|
2013-03-19 03:50:56 +01:00
|
|
|
|
|
|
|
|
/* The caller has checked the parms_ size to make sure it
|
|
|
|
|
matches the task definition, so we can just use the task
|
|
|
|
|
definition to get the parm_count. */
|
|
|
|
|
|
|
|
|
|
unsigned parm_count = def->port_count();
|
|
|
|
|
|
2013-09-21 05:38:53 +02:00
|
|
|
if (parms_.size() > parm_count) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Too many arguments (" << parms_.size()
|
|
|
|
|
<< ", expecting " << parm_count << ")"
|
|
|
|
|
<< " in call to task." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2002-05-27 02:08:45 +02:00
|
|
|
NetBlock*block = new NetBlock(NetBlock::SEQU, 0);
|
2011-02-10 06:03:08 +01:00
|
|
|
block->set_line(*this);
|
2000-04-28 18:50:53 +02:00
|
|
|
|
2000-04-29 01:12:12 +02:00
|
|
|
/* Detect the case where the definition of the task is known
|
2000-04-29 00:17:47 +02:00
|
|
|
empty. In this case, we need not bother with calls to the
|
|
|
|
|
task, all the assignments, etc. Just return a no-op. */
|
|
|
|
|
|
|
|
|
|
if (const NetBlock*tp = dynamic_cast<const NetBlock*>(def->proc())) {
|
|
|
|
|
if (tp->proc_first() == 0)
|
|
|
|
|
return block;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-27 01:54:13 +02:00
|
|
|
/* If this is an automatic task, generate a statement to
|
|
|
|
|
allocate the local storage. */
|
2013-03-19 03:50:56 +01:00
|
|
|
|
2008-09-27 01:54:13 +02:00
|
|
|
if (task->is_auto()) {
|
|
|
|
|
NetAlloc*ap = new NetAlloc(task);
|
2011-02-10 06:03:08 +01:00
|
|
|
ap->set_line(*this);
|
2008-09-27 01:54:13 +02:00
|
|
|
block->append(ap);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-19 03:50:56 +01:00
|
|
|
/* If this is a method call, then the use_this pointer will
|
|
|
|
|
have an expression for the "this" argument. The "this"
|
|
|
|
|
argument is the first argument of any method, so emit it
|
|
|
|
|
here. */
|
|
|
|
|
|
|
|
|
|
if (use_this) {
|
|
|
|
|
ivl_assert(*this, def->port_count() >= 1);
|
|
|
|
|
NetNet*port = def->port(0);
|
|
|
|
|
ivl_assert(*this, port->port_type()==NetNet::PINPUT);
|
|
|
|
|
|
|
|
|
|
NetAssign_*lv = new NetAssign_(port);
|
|
|
|
|
NetAssign*pr = new NetAssign(lv, use_this);
|
|
|
|
|
pr->set_line(*this);
|
|
|
|
|
block->append(pr);
|
|
|
|
|
}
|
|
|
|
|
|
1999-07-24 04:11:19 +02:00
|
|
|
/* Generate assignment statement statements for the input and
|
1999-07-24 21:19:06 +02:00
|
|
|
INOUT ports of the task. These are managed by writing
|
|
|
|
|
assignments with the task port the l-value and the passed
|
2000-04-28 18:50:53 +02:00
|
|
|
expression the r-value. We know by definition that the port
|
|
|
|
|
is a reg type, so this elaboration is pretty obvious. */
|
|
|
|
|
|
2013-03-19 03:50:56 +01:00
|
|
|
for (unsigned idx = use_this?1:0 ; idx < parm_count ; idx += 1) {
|
|
|
|
|
|
|
|
|
|
size_t parms_idx = use_this? idx-1 : idx;
|
2012-03-10 18:50:41 +01:00
|
|
|
|
1999-07-24 04:11:19 +02:00
|
|
|
NetNet*port = def->port(idx);
|
|
|
|
|
assert(port->port_type() != NetNet::NOT_A_PORT);
|
|
|
|
|
if (port->port_type() == NetNet::POUTPUT)
|
|
|
|
|
continue;
|
|
|
|
|
|
2001-08-26 01:50:02 +02:00
|
|
|
NetAssign_*lv = new NetAssign_(port);
|
2005-03-05 06:38:33 +01:00
|
|
|
unsigned wid = count_lval_width(lv);
|
2008-12-10 06:52:15 +01:00
|
|
|
ivl_variable_type_t lv_type = lv->expr_type();
|
2005-03-05 06:38:33 +01:00
|
|
|
|
2013-09-15 22:44:29 +02:00
|
|
|
NetExpr*rv = 0;
|
|
|
|
|
|
2015-06-20 23:39:55 +02:00
|
|
|
if (parms_idx < parms_.size() && parms_[parms_idx]) {
|
2013-09-29 23:48:42 +02:00
|
|
|
rv = elaborate_rval_expr(des, scope, port->net_type(),
|
|
|
|
|
lv_type, wid, parms_ [parms_idx]);
|
2013-09-15 22:44:29 +02:00
|
|
|
if (NetEEvent*evt = dynamic_cast<NetEEvent*> (rv)) {
|
|
|
|
|
cerr << evt->get_fileline() << ": error: An event '"
|
|
|
|
|
<< evt->event()->name() << "' can not be a user "
|
|
|
|
|
"task argument." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (def->port_defe(idx)) {
|
|
|
|
|
if (! gn_system_verilog()) {
|
2015-06-21 10:05:39 +02:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
2013-09-15 22:44:29 +02:00
|
|
|
<< "Found (and using) default task expression "
|
2015-06-20 22:39:45 +02:00
|
|
|
"requires SystemVerilog." << endl;
|
2013-09-15 22:44:29 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
rv = def->port_defe(idx);
|
2013-09-21 05:38:53 +02:00
|
|
|
if (lv_type==IVL_VT_BOOL||lv_type==IVL_VT_LOGIC)
|
|
|
|
|
rv = pad_to_width(rv->dup_expr(), wid, *this);
|
2013-09-15 22:44:29 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Missing argument " << (idx+1)
|
|
|
|
|
<< " of call to task." << endl;
|
2009-09-12 04:59:11 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2008-11-27 00:37:38 +01:00
|
|
|
|
2000-09-03 01:40:12 +02:00
|
|
|
NetAssign*pr = new NetAssign(lv, rv);
|
2011-02-10 06:03:08 +01:00
|
|
|
pr->set_line(*this);
|
1999-07-24 04:11:19 +02:00
|
|
|
block->append(pr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Generate the task call proper... */
|
2013-03-19 03:50:56 +01:00
|
|
|
NetUTask*cur = new NetUTask(task);
|
2007-04-15 22:45:40 +02:00
|
|
|
cur->set_line(*this);
|
1999-07-24 04:11:19 +02:00
|
|
|
block->append(cur);
|
|
|
|
|
|
2000-04-28 18:50:53 +02:00
|
|
|
/* Generate assignment statements for the output and INOUT
|
|
|
|
|
ports of the task. The l-value in this case is the
|
1999-09-19 00:23:50 +02:00
|
|
|
expression passed as a parameter, and the r-value is the
|
2000-04-28 18:50:53 +02:00
|
|
|
port to be copied out.
|
|
|
|
|
|
|
|
|
|
We know by definition that the r-value of this copy-out is
|
|
|
|
|
the port, which is a reg. The l-value, however, may be any
|
|
|
|
|
expression that can be a target to a procedural
|
|
|
|
|
assignment, including a memory word. */
|
|
|
|
|
|
2012-03-10 18:50:41 +01:00
|
|
|
for (unsigned idx = 0 ; idx < parm_count ; idx += 1) {
|
1999-07-24 04:11:19 +02:00
|
|
|
|
|
|
|
|
NetNet*port = def->port(idx);
|
2000-04-28 18:50:53 +02:00
|
|
|
|
|
|
|
|
/* Skip input ports. */
|
1999-07-24 04:11:19 +02:00
|
|
|
assert(port->port_type() != NetNet::NOT_A_PORT);
|
|
|
|
|
if (port->port_type() == NetNet::PINPUT)
|
|
|
|
|
continue;
|
|
|
|
|
|
2000-04-28 18:50:53 +02:00
|
|
|
|
2002-03-09 05:02:26 +01:00
|
|
|
/* Elaborate an l-value version of the port expression
|
|
|
|
|
for output and inout ports. If the expression does
|
2015-06-20 23:39:55 +02:00
|
|
|
not exist or is not a valid l-value print an error
|
|
|
|
|
message. Note that the elaborate_lval method already
|
|
|
|
|
printed a detailed message for the latter case. */
|
|
|
|
|
NetAssign_*lv = 0;
|
|
|
|
|
if (idx < parms_.size() && parms_[idx]) {
|
2013-05-18 20:21:37 +02:00
|
|
|
lv = parms_[idx]->elaborate_lval(des, scope, false, false);
|
2002-03-09 05:02:26 +01:00
|
|
|
if (lv == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << parms_[idx]->get_fileline() << ": error: "
|
2002-03-09 05:02:26 +01:00
|
|
|
<< "I give up on task port " << (idx+1)
|
|
|
|
|
<< " expression: " << *parms_[idx] << endl;
|
|
|
|
|
}
|
2015-06-20 23:39:55 +02:00
|
|
|
} else if (port->port_type() == NetNet::POUTPUT) {
|
|
|
|
|
// Output ports were skipped earlier, so
|
|
|
|
|
// report the error now.
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Missing argument " << (idx+1)
|
|
|
|
|
<< " of call to task." << endl;
|
|
|
|
|
des->errors += 1;
|
2002-03-09 05:02:26 +01:00
|
|
|
}
|
|
|
|
|
|
2001-01-10 04:13:23 +01:00
|
|
|
if (lv == 0)
|
|
|
|
|
continue;
|
1999-07-24 21:19:06 +02:00
|
|
|
|
2013-02-25 23:13:05 +01:00
|
|
|
NetExpr*rv = new NetESignal(port);
|
|
|
|
|
|
|
|
|
|
/* Handle any implicit cast. */
|
|
|
|
|
unsigned lv_width = count_lval_width(lv);
|
|
|
|
|
if (lv->expr_type() != rv->expr_type()) {
|
|
|
|
|
switch (lv->expr_type()) {
|
|
|
|
|
case IVL_VT_REAL:
|
|
|
|
|
rv = cast_to_real(rv);
|
|
|
|
|
break;
|
|
|
|
|
case IVL_VT_BOOL:
|
|
|
|
|
rv = cast_to_int2(rv, lv_width);
|
|
|
|
|
break;
|
|
|
|
|
case IVL_VT_LOGIC:
|
|
|
|
|
rv = cast_to_int4(rv, lv_width);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* Don't yet know how to handle this. */
|
|
|
|
|
ivl_assert(*this, 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rv = pad_to_width(rv, lv_width, *this);
|
1999-07-24 21:19:06 +02:00
|
|
|
|
|
|
|
|
/* Generate the assignment statement. */
|
2005-05-17 22:56:55 +02:00
|
|
|
NetAssign*ass = new NetAssign(lv, rv);
|
2011-02-10 06:03:08 +01:00
|
|
|
ass->set_line(*this);
|
1999-07-24 21:19:06 +02:00
|
|
|
|
|
|
|
|
block->append(ass);
|
1999-07-24 04:11:19 +02:00
|
|
|
}
|
|
|
|
|
|
2008-09-27 01:54:13 +02:00
|
|
|
/* If this is an automatic task, generate a statement to free
|
|
|
|
|
the local storage. */
|
|
|
|
|
if (task->is_auto()) {
|
|
|
|
|
NetFree*fp = new NetFree(task);
|
2011-02-10 06:03:08 +01:00
|
|
|
fp->set_line(*this);
|
2008-09-27 01:54:13 +02:00
|
|
|
block->append(fp);
|
|
|
|
|
}
|
|
|
|
|
|
1999-07-24 04:11:19 +02:00
|
|
|
return block;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
/*
|
2004-12-12 19:13:39 +01:00
|
|
|
* Elaborate a procedural continuous assign. This really looks very
|
|
|
|
|
* much like other procedural assignments, at this point, but there
|
2004-12-11 03:31:25 +01:00
|
|
|
* is no delay to worry about. The code generator will take care of
|
|
|
|
|
* the differences between continuous assign and normal assignments.
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetCAssign* PCAssign::elaborate(Design*des, NetScope*scope) const
|
2000-05-12 01:37:26 +02:00
|
|
|
{
|
2004-12-11 03:31:25 +01:00
|
|
|
NetCAssign*dev = 0;
|
2000-05-12 01:37:26 +02:00
|
|
|
assert(scope);
|
|
|
|
|
|
2008-11-09 01:26:55 +01:00
|
|
|
if (scope->is_auto() && lval_->has_aa_term(des, scope)) {
|
|
|
|
|
cerr << get_fileline() << ": error: automatically allocated "
|
|
|
|
|
"variables may not be assigned values using procedural "
|
|
|
|
|
"continuous assignments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scope->is_auto() && expr_->has_aa_term(des, scope)) {
|
|
|
|
|
cerr << get_fileline() << ": error: automatically allocated "
|
|
|
|
|
"variables may not be referenced in procedural "
|
|
|
|
|
"continuous assignments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-18 20:21:37 +02:00
|
|
|
NetAssign_*lval = lval_->elaborate_lval(des, scope, true, false);
|
2000-05-12 01:37:26 +02:00
|
|
|
if (lval == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
unsigned lwid = count_lval_width(lval);
|
2008-11-27 00:37:38 +01:00
|
|
|
ivl_variable_type_t ltype = lval->expr_type();
|
2006-06-02 06:48:49 +02:00
|
|
|
|
2013-09-29 23:48:42 +02:00
|
|
|
// Need to figure out a better thing to do about the
|
|
|
|
|
// lv_net_type argument to elaborate_rval_expr here. This
|
|
|
|
|
// would entail getting the NetAssign_ to give us an
|
|
|
|
|
// ivl_type_t as needed.
|
|
|
|
|
NetExpr*rexp = elaborate_rval_expr(des, scope, 0, ltype, lwid, expr_);
|
2004-12-11 03:31:25 +01:00
|
|
|
if (rexp == 0)
|
2000-05-12 01:37:26 +02:00
|
|
|
return 0;
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
dev = new NetCAssign(lval, rexp);
|
2000-05-12 01:37:26 +02:00
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Elaborate cassign,"
|
2004-12-12 19:13:39 +01:00
|
|
|
<< " lval width=" << lwid
|
2004-12-11 03:31:25 +01:00
|
|
|
<< " rval width=" << rexp->expr_width()
|
|
|
|
|
<< " rval=" << *rexp
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
2000-05-12 01:37:26 +02:00
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
dev->set_line(*this);
|
2000-05-12 01:37:26 +02:00
|
|
|
return dev;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
NetDeassign* PDeassign::elaborate(Design*des, NetScope*scope) const
|
2000-05-12 01:37:26 +02:00
|
|
|
{
|
|
|
|
|
assert(scope);
|
|
|
|
|
|
2008-11-09 01:26:55 +01:00
|
|
|
if (scope->is_auto() && lval_->has_aa_term(des, scope)) {
|
|
|
|
|
cerr << get_fileline() << ": error: automatically allocated "
|
|
|
|
|
"variables may not be assigned values using procedural "
|
|
|
|
|
"continuous assignments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-18 20:21:37 +02:00
|
|
|
NetAssign_*lval = lval_->elaborate_lval(des, scope, true, false);
|
2000-05-12 01:37:26 +02:00
|
|
|
if (lval == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
NetDeassign*dev = new NetDeassign(lval);
|
|
|
|
|
dev->set_line( *this );
|
|
|
|
|
return dev;
|
|
|
|
|
}
|
|
|
|
|
|
2000-07-07 06:53:53 +02:00
|
|
|
/*
|
2003-01-27 06:09:17 +01:00
|
|
|
* Elaborate the delay statement (of the form #<expr> <statement>) as a
|
2000-07-07 06:53:53 +02:00
|
|
|
* NetPDelay object. If the expression is constant, evaluate it now
|
|
|
|
|
* and make a constant delay. If not, then pass an elaborated
|
|
|
|
|
* expression to the constructor of NetPDelay so that the code
|
|
|
|
|
* generator knows to evaluate the expression at run time.
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PDelayStatement::elaborate(Design*des, NetScope*scope) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2000-07-07 06:53:53 +02:00
|
|
|
assert(scope);
|
|
|
|
|
|
2007-08-25 03:48:24 +02:00
|
|
|
if (scope->in_func()) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: functions cannot have "
|
2007-08-25 03:48:24 +02:00
|
|
|
"delay statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-30 08:42:26 +02:00
|
|
|
if (scope->in_final()) {
|
|
|
|
|
cerr << get_fileline() << ": error: final procedures cannot "
|
|
|
|
|
"have delay statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-02-08 20:49:21 +01:00
|
|
|
/* This call evaluates the delay expression to a NetEConst, if
|
|
|
|
|
possible. This includes transforming NetECReal values to
|
|
|
|
|
integers, and applying the proper scaling. */
|
|
|
|
|
NetExpr*dex = elaborate_delay_expr(delay_, des, scope);
|
2001-01-15 00:04:55 +01:00
|
|
|
|
2011-02-10 06:03:08 +01:00
|
|
|
NetPDelay *obj;
|
2003-02-08 20:49:21 +01:00
|
|
|
if (NetEConst*tmp = dynamic_cast<NetEConst*>(dex)) {
|
2000-12-10 23:01:35 +01:00
|
|
|
if (statement_)
|
2011-02-10 06:03:08 +01:00
|
|
|
obj = new NetPDelay(tmp->value().as_ulong64(),
|
|
|
|
|
statement_->elaborate(des, scope));
|
2000-12-10 23:01:35 +01:00
|
|
|
else
|
2011-02-10 06:03:08 +01:00
|
|
|
obj = new NetPDelay(tmp->value().as_ulong64(), 0);
|
2000-12-10 23:01:35 +01:00
|
|
|
|
2003-02-08 20:49:21 +01:00
|
|
|
delete dex;
|
2002-12-21 20:42:17 +01:00
|
|
|
|
2003-02-08 20:49:21 +01:00
|
|
|
} else {
|
2000-07-07 06:53:53 +02:00
|
|
|
if (statement_)
|
2011-02-10 06:03:08 +01:00
|
|
|
obj = new NetPDelay(dex, statement_->elaborate(des, scope));
|
2000-07-07 06:53:53 +02:00
|
|
|
else
|
2011-02-10 06:03:08 +01:00
|
|
|
obj = new NetPDelay(dex, 0);
|
1999-09-30 19:22:33 +02:00
|
|
|
}
|
2011-02-10 06:03:08 +01:00
|
|
|
obj->set_line(*this);
|
|
|
|
|
return obj;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2000-07-26 07:08:07 +02:00
|
|
|
/*
|
|
|
|
|
* The disable statement is not yet supported.
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PDisable::elaborate(Design*des, NetScope*scope) const
|
2000-07-26 07:08:07 +02:00
|
|
|
{
|
2000-07-27 07:13:44 +02:00
|
|
|
assert(scope);
|
|
|
|
|
|
2013-10-18 04:33:57 +02:00
|
|
|
/* If the disable scope_ is empty then this is a SystemVerilog
|
|
|
|
|
* disable fork statement. */
|
|
|
|
|
if (scope_.empty()) {
|
|
|
|
|
if (gn_system_verilog()) {
|
|
|
|
|
NetDisable*obj = new NetDisable(0);
|
|
|
|
|
obj->set_line(*this);
|
|
|
|
|
return obj;
|
|
|
|
|
} else {
|
|
|
|
|
cerr << get_fileline()
|
|
|
|
|
<< ": error: 'disable fork' requires SystemVerilog."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-02 05:42:12 +02:00
|
|
|
list<hname_t> spath = eval_scope_path(des, scope, scope_);
|
|
|
|
|
|
|
|
|
|
NetScope*target = des->find_scope(scope, spath);
|
2000-07-27 07:13:44 +02:00
|
|
|
if (target == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Cannot find scope "
|
2007-06-02 05:42:12 +02:00
|
|
|
<< scope_ << " in " << scope_path(scope) << endl;
|
2000-07-27 07:13:44 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (target->type()) {
|
|
|
|
|
case NetScope::FUNC:
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Cannot disable functions." << endl;
|
2000-07-27 07:13:44 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case NetScope::MODULE:
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Cannot disable modules." << endl;
|
2000-07-27 07:13:44 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetDisable*obj = new NetDisable(target);
|
|
|
|
|
obj->set_line(*this);
|
|
|
|
|
return obj;
|
2000-07-26 07:08:07 +02:00
|
|
|
}
|
|
|
|
|
|
2013-09-17 05:01:06 +02:00
|
|
|
/*
|
|
|
|
|
* The do/while loop is fairly directly represented in the netlist.
|
|
|
|
|
*/
|
|
|
|
|
NetProc* PDoWhile::elaborate(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
2015-08-17 22:47:15 +02:00
|
|
|
NetExpr*ce = elab_and_eval(des, scope, cond_, -1);
|
|
|
|
|
NetProc*sub;
|
|
|
|
|
if (statement_)
|
|
|
|
|
sub = statement_->elaborate(des, scope);
|
|
|
|
|
else
|
|
|
|
|
sub = new NetBlock(NetBlock::SEQU, 0);
|
|
|
|
|
if (ce == 0 || sub == 0) {
|
|
|
|
|
delete ce;
|
|
|
|
|
delete sub;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
NetDoWhile*loop = new NetDoWhile(ce, sub);
|
2013-09-17 05:01:06 +02:00
|
|
|
loop->set_line(*this);
|
|
|
|
|
return loop;
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
/*
|
2000-04-10 07:26:05 +02:00
|
|
|
* An event statement is an event delay of some sort, attached to a
|
|
|
|
|
* statement. Some Verilog examples are:
|
|
|
|
|
*
|
|
|
|
|
* @(posedge CLK) $display("clock rise");
|
|
|
|
|
* @event_1 $display("event triggered.");
|
|
|
|
|
* @(data or negedge clk) $display("data or clock fall.");
|
|
|
|
|
*
|
|
|
|
|
* The elaborated netlist uses the NetEvent, NetEvWait and NetEvProbe
|
|
|
|
|
* classes. The NetEvWait class represents the part of the netlist
|
|
|
|
|
* that is executed by behavioral code. The process starts waiting on
|
|
|
|
|
* the NetEvent when it executes the NetEvWait step. Net NetEvProbe
|
2003-01-27 06:09:17 +01:00
|
|
|
* and NetEvTrig are structural and behavioral equivalents that
|
2000-04-10 07:26:05 +02:00
|
|
|
* trigger the event, and awakens any processes blocking in the
|
|
|
|
|
* associated wait.
|
|
|
|
|
*
|
|
|
|
|
* The basic data structure is:
|
1999-02-01 01:26:48 +01:00
|
|
|
*
|
2000-04-10 07:26:05 +02:00
|
|
|
* NetEvWait ---/---> NetEvent <----\---- NetEvProbe
|
|
|
|
|
* ... | | ...
|
2000-04-12 06:23:57 +02:00
|
|
|
* NetEvWait ---+ +---- NetEvProbe
|
|
|
|
|
* | ...
|
|
|
|
|
* +---- NetEvTrig
|
2000-04-10 07:26:05 +02:00
|
|
|
*
|
|
|
|
|
* That is, many NetEvWait statements may wait on a single NetEvent
|
|
|
|
|
* object, and Many NetEvProbe objects may trigger the NetEvent
|
2000-04-12 06:23:57 +02:00
|
|
|
* object. The many NetEvWait objects pointing to the NetEvent object
|
|
|
|
|
* reflects the possibility of different places in the code blocking
|
|
|
|
|
* on the same named event, like so:
|
|
|
|
|
*
|
|
|
|
|
* event foo;
|
|
|
|
|
* [...]
|
|
|
|
|
* always begin @foo <statement1>; @foo <statement2> end
|
|
|
|
|
*
|
|
|
|
|
* This tends to not happen with signal edges. The multiple probes
|
|
|
|
|
* pointing to the same event reflect the possibility of many
|
|
|
|
|
* expressions in the same blocking statement, like so:
|
|
|
|
|
*
|
|
|
|
|
* wire reset, clk;
|
|
|
|
|
* [...]
|
|
|
|
|
* always @(reset or posedge clk) <stmt>;
|
|
|
|
|
*
|
|
|
|
|
* Conjunctions like this cause a NetEvent object be created to
|
2003-01-27 06:09:17 +01:00
|
|
|
* represent the overall conjunction, and NetEvProbe objects for each
|
2000-04-12 06:23:57 +02:00
|
|
|
* event expression.
|
|
|
|
|
*
|
|
|
|
|
* If the NetEvent object represents a named event from the source,
|
|
|
|
|
* then there are NetEvTrig objects that represent the trigger
|
|
|
|
|
* statements instead of the NetEvProbe objects representing signals.
|
|
|
|
|
* For example:
|
|
|
|
|
*
|
|
|
|
|
* event foo;
|
|
|
|
|
* always @foo <stmt>;
|
|
|
|
|
* initial begin
|
|
|
|
|
* [...]
|
|
|
|
|
* -> foo;
|
|
|
|
|
* [...]
|
|
|
|
|
* -> foo;
|
|
|
|
|
* [...]
|
|
|
|
|
* end
|
|
|
|
|
*
|
|
|
|
|
* Each trigger statement in the source generates a separate NetEvTrig
|
|
|
|
|
* object in the netlist. Those trigger objects are elaborated
|
|
|
|
|
* elsewhere.
|
|
|
|
|
*
|
|
|
|
|
* Additional complications arise when named events show up in
|
|
|
|
|
* conjunctions. An example of such a case is:
|
|
|
|
|
*
|
|
|
|
|
* event foo;
|
|
|
|
|
* wire bar;
|
|
|
|
|
* always @(foo or posedge bar) <stmt>;
|
|
|
|
|
*
|
|
|
|
|
* Since there is by definition a NetEvent object for the foo object,
|
|
|
|
|
* this is handled by allowing the NetEvWait object to point to
|
|
|
|
|
* multiple NetEvent objects. All the NetEvProbe based objects are
|
|
|
|
|
* collected and pointed as the synthetic NetEvent object, and all the
|
|
|
|
|
* named events are added into the list of NetEvent object that the
|
|
|
|
|
* NetEvWait object can refer to.
|
1998-11-04 00:28:49 +01:00
|
|
|
*/
|
2000-04-12 06:23:57 +02:00
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope,
|
2000-12-02 00:52:49 +01:00
|
|
|
NetProc*enet) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2000-04-04 05:20:15 +02:00
|
|
|
assert(scope);
|
|
|
|
|
|
2007-08-25 03:48:24 +02:00
|
|
|
if (scope->in_func()) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: functions cannot have "
|
2007-08-25 03:48:24 +02:00
|
|
|
"event statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-30 08:42:26 +02:00
|
|
|
if (scope->in_final()) {
|
|
|
|
|
cerr << get_fileline() << ": error: final procedures cannot "
|
|
|
|
|
"have event statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-27 06:09:17 +01:00
|
|
|
/* Create a single NetEvent and NetEvWait. Then, create a
|
2000-04-10 07:26:05 +02:00
|
|
|
NetEvProbe for each conjunctive event in the event
|
2003-01-27 06:09:17 +01:00
|
|
|
list. The NetEvProbe objects all refer back to the NetEvent
|
2000-04-10 07:26:05 +02:00
|
|
|
object. */
|
|
|
|
|
|
2004-02-18 18:11:54 +01:00
|
|
|
NetEvent*ev = new NetEvent(scope->local_symbol());
|
2000-04-12 06:23:57 +02:00
|
|
|
ev->set_line(*this);
|
|
|
|
|
unsigned expr_count = 0;
|
2000-04-10 07:26:05 +02:00
|
|
|
|
2000-04-12 06:23:57 +02:00
|
|
|
NetEvWait*wa = new NetEvWait(enet);
|
|
|
|
|
wa->set_line(*this);
|
1999-05-01 04:57:52 +02:00
|
|
|
|
2002-04-21 06:59:07 +02:00
|
|
|
/* If there are no expressions, this is a signal that it is an
|
|
|
|
|
@* statement. Generate an expression to use. */
|
|
|
|
|
|
|
|
|
|
if (expr_.count() == 0) {
|
|
|
|
|
assert(enet);
|
2007-10-25 22:31:00 +02:00
|
|
|
/* For synthesis we want just the inputs, but for the rest we
|
|
|
|
|
* want inputs and outputs that may cause a value to change. */
|
2007-10-31 03:14:09 +01:00
|
|
|
extern bool synthesis; /* Synthesis flag from main.cc */
|
2007-10-25 22:31:00 +02:00
|
|
|
bool rem_out = false;
|
2007-10-31 03:14:09 +01:00
|
|
|
if (synthesis) {
|
2007-10-25 22:31:00 +02:00
|
|
|
rem_out = true;
|
|
|
|
|
}
|
|
|
|
|
NexusSet*nset = enet->nex_input(rem_out);
|
2002-04-21 06:59:07 +02:00
|
|
|
if (nset == 0) {
|
2008-08-14 21:40:53 +02:00
|
|
|
cerr << get_fileline() << ": error: Unable to elaborate:"
|
|
|
|
|
<< endl;
|
2002-04-21 06:59:07 +02:00
|
|
|
enet->dump(cerr, 6);
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return enet;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-09 03:18:31 +02:00
|
|
|
if (nset->size() == 0) {
|
2011-11-24 02:25:30 +01:00
|
|
|
cerr << get_fileline() << ": warning: @* found no "
|
|
|
|
|
"sensitivities so it will never trigger."
|
|
|
|
|
<< endl;
|
|
|
|
|
/* Add the currently unreferenced event to the scope. */
|
|
|
|
|
scope->add_event(ev);
|
|
|
|
|
/* Delete the current wait, create a new one with no
|
|
|
|
|
* statement and add the event to it. This creates a
|
|
|
|
|
* perpetual wait since nothing will ever trigger the
|
|
|
|
|
* unreferenced event. */
|
|
|
|
|
delete wa;
|
|
|
|
|
wa = new NetEvWait(0);
|
|
|
|
|
wa->set_line(*this);
|
|
|
|
|
wa->add_event(ev);
|
|
|
|
|
return wa;
|
2002-04-21 06:59:07 +02:00
|
|
|
}
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetEvProbe*pr = new NetEvProbe(scope, scope->local_symbol(),
|
2002-04-21 06:59:07 +02:00
|
|
|
ev, NetEvProbe::ANYEDGE,
|
2013-09-09 03:18:31 +02:00
|
|
|
nset->size());
|
|
|
|
|
for (unsigned idx = 0 ; idx < nset->size() ; idx += 1)
|
2014-02-15 23:16:22 +01:00
|
|
|
connect(nset->at(idx).lnk, pr->pin(idx));
|
2002-04-21 06:59:07 +02:00
|
|
|
|
|
|
|
|
delete nset;
|
|
|
|
|
des->add_node(pr);
|
|
|
|
|
|
|
|
|
|
expr_count = 1;
|
|
|
|
|
|
|
|
|
|
} else for (unsigned idx = 0 ; idx < expr_.count() ; idx += 1) {
|
2000-04-12 06:23:57 +02:00
|
|
|
|
|
|
|
|
assert(expr_[idx]->expr());
|
|
|
|
|
|
|
|
|
|
/* If the expression is an identifier that matches a
|
2008-10-21 18:00:48 +02:00
|
|
|
named event, then handle this case all at once and
|
2000-04-12 06:23:57 +02:00
|
|
|
skip the rest of the expression handling. */
|
2000-04-02 00:14:19 +02:00
|
|
|
|
2000-04-09 18:43:50 +02:00
|
|
|
if (PEIdent*id = dynamic_cast<PEIdent*>(expr_[idx]->expr())) {
|
2003-09-20 03:05:35 +02:00
|
|
|
NetNet* sig = 0;
|
|
|
|
|
const NetExpr*par = 0;
|
|
|
|
|
NetEvent* eve = 0;
|
|
|
|
|
|
2008-10-28 18:52:39 +01:00
|
|
|
NetScope*found_in = symbol_search(this, des, scope,
|
|
|
|
|
id->path(),
|
2007-01-16 06:44:14 +01:00
|
|
|
sig, par, eve);
|
2003-09-20 03:05:35 +02:00
|
|
|
|
|
|
|
|
if (found_in && eve) {
|
|
|
|
|
wa->add_event(eve);
|
2010-03-13 20:12:00 +01:00
|
|
|
/* You can not look for the posedge or negedge of
|
|
|
|
|
* an event. */
|
|
|
|
|
if (expr_[idx]->type() != PEEvent::ANYEDGE) {
|
|
|
|
|
cerr << get_fileline() << ": error: ";
|
|
|
|
|
switch (expr_[idx]->type()) {
|
|
|
|
|
case PEEvent::POSEDGE:
|
|
|
|
|
cerr << "posedge";
|
|
|
|
|
break;
|
|
|
|
|
case PEEvent::NEGEDGE:
|
|
|
|
|
cerr << "negedge";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
cerr << "unknown edge type!";
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
cerr << " can not be used with a named event ("
|
|
|
|
|
<< eve->name() << ")." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
2000-04-12 06:23:57 +02:00
|
|
|
continue;
|
2000-04-09 18:43:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-20 03:05:35 +02:00
|
|
|
|
2000-04-12 06:23:57 +02:00
|
|
|
/* So now we have a normal event expression. Elaborate
|
|
|
|
|
the sub-expression as a net and decide how to handle
|
|
|
|
|
the edge. */
|
|
|
|
|
|
2008-11-09 01:26:55 +01:00
|
|
|
if (scope->is_auto()) {
|
|
|
|
|
if (! dynamic_cast<PEIdent*>(expr_[idx]->expr())) {
|
|
|
|
|
cerr << get_fileline() << ": sorry, complex event "
|
|
|
|
|
"expressions are not yet supported in "
|
|
|
|
|
"automatic tasks." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
NetExpr*tmp = elab_and_eval(des, scope, expr_[idx]->expr(), -1);
|
2008-09-09 04:13:49 +02:00
|
|
|
if (tmp == 0) {
|
|
|
|
|
expr_[idx]->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-22 22:48:34 +01:00
|
|
|
NetNet*expr = tmp->synthesize(des, scope, tmp);
|
1999-05-01 04:57:52 +02:00
|
|
|
if (expr == 0) {
|
2000-04-10 07:26:05 +02:00
|
|
|
expr_[idx]->dump(cerr);
|
1999-05-01 04:57:52 +02:00
|
|
|
cerr << endl;
|
1999-05-10 02:16:57 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
1999-05-01 04:57:52 +02:00
|
|
|
}
|
|
|
|
|
assert(expr);
|
1999-05-01 22:43:55 +02:00
|
|
|
|
2008-09-09 04:13:49 +02:00
|
|
|
delete tmp;
|
|
|
|
|
|
2000-04-12 06:23:57 +02:00
|
|
|
unsigned pins = (expr_[idx]->type() == PEEvent::ANYEDGE)
|
1999-05-01 22:43:55 +02:00
|
|
|
? expr->pin_count() : 1;
|
|
|
|
|
|
2000-04-10 07:26:05 +02:00
|
|
|
NetEvProbe*pr;
|
|
|
|
|
switch (expr_[idx]->type()) {
|
2000-04-12 06:23:57 +02:00
|
|
|
case PEEvent::POSEDGE:
|
2003-03-06 01:28:41 +01:00
|
|
|
pr = new NetEvProbe(scope, scope->local_symbol(), ev,
|
2000-04-10 07:26:05 +02:00
|
|
|
NetEvProbe::POSEDGE, pins);
|
|
|
|
|
break;
|
|
|
|
|
|
2000-04-12 06:23:57 +02:00
|
|
|
case PEEvent::NEGEDGE:
|
2003-03-06 01:28:41 +01:00
|
|
|
pr = new NetEvProbe(scope, scope->local_symbol(), ev,
|
2000-04-10 07:26:05 +02:00
|
|
|
NetEvProbe::NEGEDGE, pins);
|
|
|
|
|
break;
|
1999-04-29 04:16:26 +02:00
|
|
|
|
2000-04-12 06:23:57 +02:00
|
|
|
case PEEvent::ANYEDGE:
|
2003-03-06 01:28:41 +01:00
|
|
|
pr = new NetEvProbe(scope, scope->local_symbol(), ev,
|
2000-04-10 07:26:05 +02:00
|
|
|
NetEvProbe::ANYEDGE, pins);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2009-12-10 22:58:50 +01:00
|
|
|
pr = NULL;
|
2000-04-10 07:26:05 +02:00
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (unsigned p = 0 ; p < pr->pin_count() ; p += 1)
|
|
|
|
|
connect(pr->pin(p), expr->pin(p));
|
|
|
|
|
|
|
|
|
|
des->add_node(pr);
|
2000-04-12 06:23:57 +02:00
|
|
|
expr_count += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If there was at least one conjunction that was an
|
|
|
|
|
expression (and not a named event) then add this
|
|
|
|
|
event. Otherwise, we didn't use it so delete it. */
|
|
|
|
|
if (expr_count > 0) {
|
2002-07-24 18:22:59 +02:00
|
|
|
scope->add_event(ev);
|
|
|
|
|
wa->add_event(ev);
|
|
|
|
|
/* NOTE: This event that I am adding to the wait may be
|
|
|
|
|
a duplicate of another event somewhere else. However,
|
|
|
|
|
I don't know that until all the modules are hooked
|
|
|
|
|
up, so it is best to leave find_similar_event to
|
|
|
|
|
after elaboration. */
|
2000-04-12 06:23:57 +02:00
|
|
|
} else {
|
|
|
|
|
delete ev;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2000-04-10 07:26:05 +02:00
|
|
|
return wa;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2003-05-19 04:50:58 +02:00
|
|
|
/*
|
|
|
|
|
* This is the special case of the event statement, the wait
|
|
|
|
|
* statement. This is elaborated into a slightly more complicated
|
|
|
|
|
* statement that uses non-wait statements:
|
|
|
|
|
*
|
|
|
|
|
* wait (<expr>) <statement>
|
|
|
|
|
*
|
|
|
|
|
* becomes
|
|
|
|
|
*
|
|
|
|
|
* begin
|
|
|
|
|
* while (1 !== <expr>)
|
|
|
|
|
* @(<expr inputs>) <noop>;
|
|
|
|
|
* <statement>;
|
|
|
|
|
* end
|
|
|
|
|
*/
|
|
|
|
|
NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope,
|
|
|
|
|
NetProc*enet) const
|
|
|
|
|
{
|
|
|
|
|
assert(scope);
|
|
|
|
|
assert(expr_.count() == 1);
|
|
|
|
|
|
2007-08-25 03:48:24 +02:00
|
|
|
if (scope->in_func()) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: functions cannot have "
|
2007-08-25 03:48:24 +02:00
|
|
|
"wait statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-30 08:42:26 +02:00
|
|
|
if (scope->in_final()) {
|
|
|
|
|
cerr << get_fileline() << ": error: final procedures cannot "
|
|
|
|
|
"have wait statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-27 00:37:38 +01:00
|
|
|
PExpr *pe = expr_[0]->expr();
|
2003-05-19 04:50:58 +02:00
|
|
|
|
2004-03-07 21:04:10 +01:00
|
|
|
/* Elaborate wait expression. Don't eval yet, we will do that
|
|
|
|
|
shortly, after we apply a reduction or. */
|
2008-11-27 00:37:38 +01:00
|
|
|
|
2011-10-05 18:49:00 +02:00
|
|
|
PExpr::width_mode_t mode = PExpr::SIZED;
|
2011-02-26 23:59:52 +01:00
|
|
|
pe->test_width(des, scope, mode);
|
2011-03-27 12:08:33 +02:00
|
|
|
NetExpr*expr = pe->elaborate_expr(des, scope, pe->expr_width(),
|
|
|
|
|
PExpr::NO_FLAGS);
|
2003-05-19 04:50:58 +02:00
|
|
|
if (expr == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Unable to elaborate"
|
2003-05-19 04:50:58 +02:00
|
|
|
" wait condition expression." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-22 17:08:14 +01:00
|
|
|
// If the condition expression is more than 1 bits, then
|
2004-05-25 05:42:58 +02:00
|
|
|
// generate a reduction operator to get the result down to
|
|
|
|
|
// one bit. In other words, Turn <e> into |<e>;
|
2004-01-13 04:42:49 +01:00
|
|
|
|
|
|
|
|
if (expr->expr_width() < 1) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
2004-01-13 04:42:49 +01:00
|
|
|
"incomprehensible wait expression width (0)." << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (expr->expr_width() > 1) {
|
|
|
|
|
assert(expr->expr_width() > 1);
|
|
|
|
|
NetEUReduce*cmp = new NetEUReduce('|', expr);
|
2013-06-27 06:24:19 +02:00
|
|
|
cmp->set_line(*pe);
|
2004-01-13 04:42:49 +01:00
|
|
|
expr = cmp;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-05 23:07:26 +02:00
|
|
|
/* precalculate as much as possible of the wait expression. */
|
2008-03-08 03:51:50 +01:00
|
|
|
eval_expr(expr);
|
2004-09-05 23:07:26 +02:00
|
|
|
|
2003-05-19 04:50:58 +02:00
|
|
|
/* Detect the unusual case that the wait expression is
|
|
|
|
|
constant. Constant true is OK (it becomes transparent) but
|
|
|
|
|
constant false is almost certainly not what is intended. */
|
2004-05-25 05:42:58 +02:00
|
|
|
assert(expr->expr_width() == 1);
|
2003-05-19 04:50:58 +02:00
|
|
|
if (NetEConst*ce = dynamic_cast<NetEConst*>(expr)) {
|
|
|
|
|
verinum val = ce->value();
|
|
|
|
|
assert(val.len() == 1);
|
2004-05-25 05:42:58 +02:00
|
|
|
|
|
|
|
|
/* Constant true -- wait(1) <s1> reduces to <s1>. */
|
2003-05-19 04:50:58 +02:00
|
|
|
if (val[0] == verinum::V1) {
|
|
|
|
|
delete expr;
|
|
|
|
|
assert(enet);
|
|
|
|
|
return enet;
|
|
|
|
|
}
|
|
|
|
|
|
2004-05-25 05:42:58 +02:00
|
|
|
/* Otherwise, false. wait(0) blocks permanently. */
|
|
|
|
|
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": warning: wait expression is "
|
2003-05-19 04:50:58 +02:00
|
|
|
<< "constant false." << endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : The statement will "
|
2003-05-19 04:50:58 +02:00
|
|
|
<< "block permanently." << endl;
|
2004-05-25 05:42:58 +02:00
|
|
|
|
|
|
|
|
/* Create an event wait and an otherwise unreferenced
|
|
|
|
|
event variable to force a perpetual wait. */
|
|
|
|
|
NetEvent*wait_event = new NetEvent(scope->local_symbol());
|
|
|
|
|
scope->add_event(wait_event);
|
|
|
|
|
|
|
|
|
|
NetEvWait*wait = new NetEvWait(0);
|
|
|
|
|
wait->add_event(wait_event);
|
|
|
|
|
wait->set_line(*this);
|
|
|
|
|
|
|
|
|
|
delete expr;
|
|
|
|
|
delete enet;
|
|
|
|
|
return wait;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Invert the sense of the test with an exclusive NOR. In
|
|
|
|
|
other words, if this adjusted expression returns TRUE, then
|
|
|
|
|
wait. */
|
|
|
|
|
assert(expr->expr_width() == 1);
|
|
|
|
|
expr = new NetEBComp('N', expr, new NetEConst(verinum(verinum::V1)));
|
2013-06-27 06:24:19 +02:00
|
|
|
expr->set_line(*pe);
|
2008-03-08 03:51:50 +01:00
|
|
|
eval_expr(expr);
|
2003-05-19 04:50:58 +02:00
|
|
|
|
2004-02-18 18:11:54 +01:00
|
|
|
NetEvent*wait_event = new NetEvent(scope->local_symbol());
|
2003-05-19 04:50:58 +02:00
|
|
|
scope->add_event(wait_event);
|
|
|
|
|
|
|
|
|
|
NetEvWait*wait = new NetEvWait(0 /* noop */);
|
|
|
|
|
wait->add_event(wait_event);
|
|
|
|
|
wait->set_line(*this);
|
|
|
|
|
|
|
|
|
|
NexusSet*wait_set = expr->nex_input();
|
|
|
|
|
if (wait_set == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: No NexusSet"
|
2003-05-19 04:50:58 +02:00
|
|
|
<< " from wait expression." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-09 03:18:31 +02:00
|
|
|
if (wait_set->size() == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: Empty NexusSet"
|
2003-05-19 04:50:58 +02:00
|
|
|
<< " from wait expression." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetEvProbe*wait_pr = new NetEvProbe(scope, scope->local_symbol(),
|
|
|
|
|
wait_event, NetEvProbe::ANYEDGE,
|
2013-09-09 03:18:31 +02:00
|
|
|
wait_set->size());
|
|
|
|
|
for (unsigned idx = 0; idx < wait_set->size() ; idx += 1)
|
2014-02-15 23:16:22 +01:00
|
|
|
connect(wait_set->at(idx).lnk, wait_pr->pin(idx));
|
2003-05-19 04:50:58 +02:00
|
|
|
|
|
|
|
|
delete wait_set;
|
|
|
|
|
des->add_node(wait_pr);
|
|
|
|
|
|
|
|
|
|
NetWhile*loop = new NetWhile(expr, wait);
|
|
|
|
|
loop->set_line(*this);
|
|
|
|
|
|
2003-09-13 03:01:51 +02:00
|
|
|
/* If there is no real substatement (i.e., "wait (foo) ;") then
|
2003-05-19 04:50:58 +02:00
|
|
|
we are done. */
|
|
|
|
|
if (enet == 0)
|
|
|
|
|
return loop;
|
|
|
|
|
|
2004-05-25 05:42:58 +02:00
|
|
|
/* Create a sequential block to combine the wait loop and the
|
2003-05-19 04:50:58 +02:00
|
|
|
delayed statement. */
|
|
|
|
|
NetBlock*block = new NetBlock(NetBlock::SEQU, 0);
|
|
|
|
|
block->append(loop);
|
|
|
|
|
block->append(enet);
|
|
|
|
|
block->set_line(*this);
|
|
|
|
|
|
|
|
|
|
return block;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-22 18:39:25 +02:00
|
|
|
/*
|
|
|
|
|
* This is a special case of the event statement, the wait fork
|
|
|
|
|
* statement. This is elaborated into a simplified statement.
|
|
|
|
|
*
|
|
|
|
|
* wait fork;
|
|
|
|
|
*
|
|
|
|
|
* becomes
|
|
|
|
|
*
|
|
|
|
|
* @(0) <noop>;
|
|
|
|
|
*/
|
|
|
|
|
NetProc* PEventStatement::elaborate_wait_fork(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
assert(scope);
|
|
|
|
|
assert(expr_.count() == 1);
|
|
|
|
|
assert(expr_[0] == 0);
|
|
|
|
|
assert(! statement_);
|
|
|
|
|
|
|
|
|
|
if (scope->in_func()) {
|
|
|
|
|
cerr << get_fileline() << ": error: functions cannot have "
|
|
|
|
|
"wait fork statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scope->in_final()) {
|
|
|
|
|
cerr << get_fileline() << ": error: final procedures cannot "
|
|
|
|
|
"have wait fork statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gn_system_verilog()) {
|
|
|
|
|
NetEvWait*wait = new NetEvWait(0 /* noop */);
|
|
|
|
|
wait->add_event(0);
|
|
|
|
|
wait->set_line(*this);
|
|
|
|
|
return wait;
|
|
|
|
|
} else {
|
|
|
|
|
cerr << get_fileline()
|
|
|
|
|
<< ": error: 'wait fork' requires SystemVerilog." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2003-05-19 04:50:58 +02:00
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PEventStatement::elaborate(Design*des, NetScope*scope) const
|
1999-09-22 04:00:48 +02:00
|
|
|
{
|
2013-10-22 18:39:25 +02:00
|
|
|
/* Check to see if this is a wait fork statement. */
|
2013-10-22 18:47:12 +02:00
|
|
|
if ((expr_.count() == 1) && (expr_[0] == 0))
|
2013-10-22 18:39:25 +02:00
|
|
|
return elaborate_wait_fork(des, scope);
|
|
|
|
|
|
1999-09-22 04:00:48 +02:00
|
|
|
NetProc*enet = 0;
|
|
|
|
|
if (statement_) {
|
2001-11-22 07:20:59 +01:00
|
|
|
enet = statement_->elaborate(des, scope);
|
1999-09-22 04:00:48 +02:00
|
|
|
if (enet == 0)
|
|
|
|
|
return 0;
|
2004-09-05 23:07:26 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
enet = new NetBlock(NetBlock::SEQU, 0);
|
|
|
|
|
enet->set_line(*this);
|
1999-09-22 04:00:48 +02:00
|
|
|
}
|
|
|
|
|
|
2003-05-19 04:50:58 +02:00
|
|
|
if ((expr_.count() == 1) && (expr_[0]->type() == PEEvent::POSITIVE))
|
|
|
|
|
return elaborate_wait(des, scope, enet);
|
|
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
return elaborate_st(des, scope, enet);
|
1999-09-22 04:00:48 +02:00
|
|
|
}
|
|
|
|
|
|
1999-06-19 23:06:16 +02:00
|
|
|
/*
|
|
|
|
|
* Forever statements are represented directly in the netlist. It is
|
|
|
|
|
* theoretically possible to use a while structure with a constant
|
|
|
|
|
* expression to represent the loop, but why complicate the code
|
|
|
|
|
* generators so?
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PForever::elaborate(Design*des, NetScope*scope) const
|
1999-06-19 23:06:16 +02:00
|
|
|
{
|
2015-08-17 22:47:15 +02:00
|
|
|
NetProc*stat;
|
|
|
|
|
if (statement_)
|
|
|
|
|
stat = statement_->elaborate(des, scope);
|
|
|
|
|
else
|
|
|
|
|
stat = new NetBlock(NetBlock::SEQU, 0);
|
1999-06-19 23:06:16 +02:00
|
|
|
if (stat == 0) return 0;
|
|
|
|
|
|
|
|
|
|
NetForever*proc = new NetForever(stat);
|
2011-02-10 06:03:08 +01:00
|
|
|
proc->set_line(*this);
|
1999-06-19 23:06:16 +02:00
|
|
|
return proc;
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-30 00:55:43 +01:00
|
|
|
/*
|
2008-01-29 21:19:59 +01:00
|
|
|
* Force is like a procedural assignment, most notably procedural
|
2004-12-30 00:55:43 +01:00
|
|
|
* continuous assignment:
|
|
|
|
|
*
|
|
|
|
|
* force <lval> = <rval>
|
|
|
|
|
*
|
|
|
|
|
* The <lval> can be anything that a normal behavioral assignment can
|
2011-03-11 20:27:54 +01:00
|
|
|
* take, plus net signals. This is a little bit more lax than the
|
2008-01-25 22:34:51 +01:00
|
|
|
* other procedural assignments.
|
2004-12-30 00:55:43 +01:00
|
|
|
*/
|
2004-12-11 03:31:25 +01:00
|
|
|
NetForce* PForce::elaborate(Design*des, NetScope*scope) const
|
2000-04-22 06:20:19 +02:00
|
|
|
{
|
2004-12-11 03:31:25 +01:00
|
|
|
NetForce*dev = 0;
|
2000-04-22 06:20:19 +02:00
|
|
|
assert(scope);
|
|
|
|
|
|
2008-11-09 01:26:55 +01:00
|
|
|
if (scope->is_auto() && lval_->has_aa_term(des, scope)) {
|
|
|
|
|
cerr << get_fileline() << ": error: automatically allocated "
|
|
|
|
|
"variables may not be assigned values using procedural "
|
|
|
|
|
"force statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (scope->is_auto() && expr_->has_aa_term(des, scope)) {
|
|
|
|
|
cerr << get_fileline() << ": error: automatically allocated "
|
|
|
|
|
"variables may not be referenced in procedural force "
|
|
|
|
|
"statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-18 20:21:37 +02:00
|
|
|
NetAssign_*lval = lval_->elaborate_lval(des, scope, false, true);
|
2000-04-22 06:20:19 +02:00
|
|
|
if (lval == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
unsigned lwid = count_lval_width(lval);
|
2008-11-27 00:37:38 +01:00
|
|
|
ivl_variable_type_t ltype = lval->expr_type();
|
2006-06-02 06:48:49 +02:00
|
|
|
|
2013-09-29 23:48:42 +02:00
|
|
|
// Like a variety of other assigns, we need to figure out a
|
|
|
|
|
// better way to get a reasonable lv_net_type value, and that
|
|
|
|
|
// probably will involve NetAssign_ having a method for
|
|
|
|
|
// synthesizing one as needed.
|
|
|
|
|
NetExpr*rexp = elaborate_rval_expr(des, scope, 0, ltype, lwid, expr_);
|
2004-12-11 03:31:25 +01:00
|
|
|
if (rexp == 0)
|
2000-04-22 06:20:19 +02:00
|
|
|
return 0;
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
dev = new NetForce(lval, rexp);
|
2000-04-22 06:20:19 +02:00
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
if (debug_elaborate) {
|
2008-06-24 17:46:16 +02:00
|
|
|
cerr << get_fileline() << ": debug: Elaborate force,"
|
2004-12-11 03:31:25 +01:00
|
|
|
<< " lval width=" << lval->lwidth()
|
|
|
|
|
<< " rval width=" << rexp->expr_width()
|
|
|
|
|
<< " rval=" << *rexp
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
2000-04-22 06:20:19 +02:00
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
dev->set_line(*this);
|
2000-04-22 06:20:19 +02:00
|
|
|
return dev;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 05:52:51 +02:00
|
|
|
static void find_property_in_class(const LineInfo&loc, const NetScope*scope, perm_string name, const netclass_t*&found_in, int&property)
|
|
|
|
|
{
|
2014-09-07 01:26:08 +02:00
|
|
|
found_in = find_class_containing_scope(loc, scope);
|
2014-09-05 05:52:51 +02:00
|
|
|
property = -1;
|
|
|
|
|
|
2014-09-07 01:26:08 +02:00
|
|
|
if (found_in==0) return;
|
2014-09-05 05:52:51 +02:00
|
|
|
|
|
|
|
|
property = found_in->property_idx_from_name(name);
|
|
|
|
|
if (property < 0) {
|
|
|
|
|
found_in = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-22 00:47:46 +02:00
|
|
|
/*
|
|
|
|
|
* The foreach statement can be written as a for statement like so:
|
|
|
|
|
*
|
|
|
|
|
* for (<idx> = $low(<array>) ; <idx> <= $high(<array>) ; <idx> += 1)
|
|
|
|
|
* <statement_>
|
|
|
|
|
*
|
|
|
|
|
* The <idx> variable is already known to be in the containing named
|
|
|
|
|
* block scope, which was created by the parser.
|
|
|
|
|
*/
|
|
|
|
|
NetProc* PForeach::elaborate(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
2014-09-05 05:52:51 +02:00
|
|
|
// Locate the signal for the array variable
|
2014-08-26 02:10:26 +02:00
|
|
|
pform_name_t array_name;
|
|
|
|
|
array_name.push_back(name_component_t(array_var_));
|
|
|
|
|
NetNet*array_sig = des->find_signal(scope, array_name);
|
2014-09-02 18:23:54 +02:00
|
|
|
|
2014-09-05 05:52:51 +02:00
|
|
|
// And if necessary, look for the class property that is
|
|
|
|
|
// referenced.
|
|
|
|
|
const netclass_t*class_scope = 0;
|
|
|
|
|
int class_property = -1;
|
|
|
|
|
if (array_sig == 0)
|
|
|
|
|
find_property_in_class(*this, scope, array_var_, class_scope, class_property);
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate && array_sig) {
|
|
|
|
|
cerr << get_fileline() << ": PForeach::elaborate: "
|
|
|
|
|
<< "Found array_sig in " << scope_path(array_sig->scope()) << "." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate && class_scope) {
|
|
|
|
|
cerr << get_fileline() << ": PForeach::elaborate: "
|
|
|
|
|
<< "Found array_sig property (" << class_property
|
|
|
|
|
<< ") in class " << class_scope->get_name()
|
|
|
|
|
<< " as " << *class_scope->get_prop_type(class_property) << "." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (class_scope!=0 && class_property >= 0) {
|
|
|
|
|
ivl_type_t ptype = class_scope->get_prop_type(class_property);
|
|
|
|
|
const netsarray_t*atype = dynamic_cast<const netsarray_t*> (ptype);
|
|
|
|
|
if (atype == 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "I can't handle the type of " << array_var_
|
|
|
|
|
<< " as a foreach loop." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const std::vector<netrange_t>&dims = atype->static_dimensions();
|
|
|
|
|
if (dims.size() < index_vars_.size()) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "class " << class_scope->get_name()
|
|
|
|
|
<< " property " << array_var_
|
|
|
|
|
<< " has too few dimensions for foreach dimension list." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return elaborate_static_array_(des, scope, dims);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-02 18:23:54 +02:00
|
|
|
if (array_sig == 0) {
|
|
|
|
|
cerr << get_fileline() << ": error:"
|
2014-09-05 05:52:51 +02:00
|
|
|
<< " Unable to find foreach array " << array_name
|
2014-09-02 18:23:54 +02:00
|
|
|
<< " in scope " << scope_path(scope)
|
|
|
|
|
<< "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-26 02:10:26 +02:00
|
|
|
ivl_assert(*this, array_sig);
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PForeach::elaborate: "
|
|
|
|
|
<< "Scan array " << array_sig->name()
|
2014-08-30 05:31:51 +02:00
|
|
|
<< " of " << array_sig->data_type()
|
|
|
|
|
<< " with " << array_sig->unpacked_dimensions() << " unpacked"
|
|
|
|
|
<< " and " << array_sig->packed_dimensions()
|
2014-08-26 02:10:26 +02:00
|
|
|
<< " packed dimensions." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-30 05:31:51 +02:00
|
|
|
// Classic arrays are processed this way.
|
2014-08-26 02:10:26 +02:00
|
|
|
if (array_sig->data_type()==IVL_VT_BOOL)
|
2014-09-05 05:52:51 +02:00
|
|
|
return elaborate_static_array_(des, scope, array_sig->unpacked_dims());
|
2014-08-26 02:10:26 +02:00
|
|
|
if (array_sig->data_type()==IVL_VT_LOGIC)
|
2014-09-05 05:52:51 +02:00
|
|
|
return elaborate_static_array_(des, scope, array_sig->unpacked_dims());
|
2014-08-30 05:31:51 +02:00
|
|
|
if (array_sig->unpacked_dimensions() >= index_vars_.size())
|
2014-09-05 05:52:51 +02:00
|
|
|
return elaborate_static_array_(des, scope, array_sig->unpacked_dims());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// At this point, we know that the array is dynamic so we
|
|
|
|
|
// handle that slightly differently, using run-time tests.
|
2014-08-26 02:10:26 +02:00
|
|
|
|
2014-08-25 23:58:57 +02:00
|
|
|
if (index_vars_.size() != 1) {
|
|
|
|
|
cerr << get_fileline() << ": sorry: "
|
|
|
|
|
<< "Multi-index foreach loops not supported." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-22 00:47:46 +02:00
|
|
|
// Get the signal for the index variable.
|
|
|
|
|
pform_name_t index_name;
|
2014-08-25 23:58:57 +02:00
|
|
|
index_name.push_back(name_component_t(index_vars_[0]));
|
2014-08-22 00:47:46 +02:00
|
|
|
NetNet*idx_sig = des->find_signal(scope, index_name);
|
|
|
|
|
ivl_assert(*this, idx_sig);
|
|
|
|
|
|
|
|
|
|
NetESignal*array_exp = new NetESignal(array_sig);
|
|
|
|
|
array_exp->set_line(*this);
|
|
|
|
|
|
2014-08-26 02:10:26 +02:00
|
|
|
NetESignal*idx_exp = new NetESignal(idx_sig);
|
|
|
|
|
idx_exp->set_line(*this);
|
|
|
|
|
|
2014-08-22 00:47:46 +02:00
|
|
|
// Make an initialization expression for the index.
|
|
|
|
|
NetESFunc*init_expr = new NetESFunc("$low", IVL_VT_BOOL, 32, 1);
|
|
|
|
|
init_expr->set_line(*this);
|
|
|
|
|
init_expr->parm(0, array_exp);
|
|
|
|
|
|
|
|
|
|
// Make a condition expression: idx <= $high(array)
|
|
|
|
|
NetESFunc*high_exp = new NetESFunc("$high", IVL_VT_BOOL, 32, 1);
|
|
|
|
|
high_exp->set_line(*this);
|
|
|
|
|
high_exp->parm(0, array_exp);
|
|
|
|
|
|
|
|
|
|
NetEBComp*cond_expr = new NetEBComp('L', idx_exp, high_exp);
|
|
|
|
|
cond_expr->set_line(*this);
|
|
|
|
|
|
|
|
|
|
/* Elaborate the statement that is contained in the foreach
|
|
|
|
|
loop. */
|
2015-08-17 22:47:15 +02:00
|
|
|
NetProc*sub;
|
|
|
|
|
if (statement_)
|
|
|
|
|
sub = statement_->elaborate(des, scope);
|
|
|
|
|
else
|
|
|
|
|
sub = new NetBlock(NetBlock::SEQU, 0);
|
2014-08-22 00:47:46 +02:00
|
|
|
|
|
|
|
|
/* Make a step statement: idx += 1 */
|
|
|
|
|
NetAssign_*idx_lv = new NetAssign_(idx_sig);
|
|
|
|
|
NetEConst*step_val = make_const_val(1);
|
|
|
|
|
NetAssign*step = new NetAssign(idx_lv, '+', step_val);
|
|
|
|
|
step->set_line(*this);
|
|
|
|
|
|
|
|
|
|
NetForLoop*stmt = new NetForLoop(idx_sig, init_expr, cond_expr, sub, step);
|
|
|
|
|
stmt->set_line(*this);
|
|
|
|
|
stmt->wrap_up();
|
|
|
|
|
|
|
|
|
|
return stmt;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-26 02:10:26 +02:00
|
|
|
/*
|
|
|
|
|
* This is a variant of the PForeach::elaborate() method that handles
|
|
|
|
|
* the case that the array has static dimensions. We can use constants
|
|
|
|
|
* and possibly do some optimizations.
|
|
|
|
|
*/
|
|
|
|
|
NetProc* PForeach::elaborate_static_array_(Design*des, NetScope*scope,
|
2014-09-05 05:52:51 +02:00
|
|
|
const vector<netrange_t>&dims) const
|
2014-08-26 02:10:26 +02:00
|
|
|
{
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PForeach::elaborate_static_array_: "
|
|
|
|
|
<< "Handle as array with static dimensions." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, index_vars_.size() > 0);
|
2014-09-05 05:52:51 +02:00
|
|
|
ivl_assert(*this, dims.size() == index_vars_.size());
|
2014-08-26 02:10:26 +02:00
|
|
|
|
2015-08-17 22:47:15 +02:00
|
|
|
NetProc*sub;
|
|
|
|
|
if (statement_)
|
|
|
|
|
sub = statement_->elaborate(des, scope);
|
|
|
|
|
else
|
|
|
|
|
sub = new NetBlock(NetBlock::SEQU, 0);
|
2014-08-26 02:10:26 +02:00
|
|
|
NetForLoop*stmt = 0;
|
|
|
|
|
|
|
|
|
|
for (int idx_idx = index_vars_.size()-1 ; idx_idx >= 0 ; idx_idx -= 1) {
|
2014-09-05 05:52:51 +02:00
|
|
|
const netrange_t&idx_range = dims[idx_idx];
|
2014-08-26 02:10:26 +02:00
|
|
|
|
|
|
|
|
// Get the $high and $low constant values for this slice
|
|
|
|
|
// of the array.
|
|
|
|
|
NetEConst*hig_expr = make_const_val_s(idx_range.get_msb());
|
|
|
|
|
NetEConst*low_expr = make_const_val_s(idx_range.get_lsb());
|
|
|
|
|
if (idx_range.get_msb() < idx_range.get_lsb()) {
|
|
|
|
|
NetEConst*tmp = hig_expr;
|
|
|
|
|
hig_expr = low_expr;
|
|
|
|
|
low_expr = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hig_expr->set_line(*this);
|
|
|
|
|
low_expr->set_line(*this);
|
|
|
|
|
|
|
|
|
|
pform_name_t idx_name;
|
|
|
|
|
idx_name.push_back(name_component_t(index_vars_[idx_idx]));
|
|
|
|
|
NetNet*idx_sig = des->find_signal(scope, idx_name);
|
|
|
|
|
ivl_assert(*this, idx_sig);
|
|
|
|
|
|
|
|
|
|
// Make the condition expression <idx> <= $high(slice)
|
|
|
|
|
NetESignal*idx_expr = new NetESignal(idx_sig);
|
|
|
|
|
idx_expr->set_line(*this);
|
|
|
|
|
|
|
|
|
|
NetEBComp*cond_expr = new NetEBComp('L', idx_expr, hig_expr);
|
|
|
|
|
cond_expr->set_line(*this);
|
|
|
|
|
|
|
|
|
|
// Make the step statement: <idx> += 1
|
|
|
|
|
NetAssign_*idx_lv = new NetAssign_(idx_sig);
|
|
|
|
|
NetEConst*step_val = make_const_val_s(1);
|
|
|
|
|
NetAssign*step = new NetAssign(idx_lv, '+', step_val);
|
|
|
|
|
step->set_line(*this);
|
|
|
|
|
|
|
|
|
|
stmt = new NetForLoop(idx_sig, low_expr, cond_expr, sub, step);
|
|
|
|
|
stmt->set_line(*this);
|
|
|
|
|
stmt->wrap_up();
|
|
|
|
|
|
|
|
|
|
sub = stmt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return stmt? stmt : sub;
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-09 19:55:33 +01:00
|
|
|
/*
|
1999-08-18 06:00:02 +02:00
|
|
|
* elaborate the for loop as the equivalent while loop. This eases the
|
1998-11-09 19:55:33 +01:00
|
|
|
* task for the target code generator. The structure is:
|
|
|
|
|
*
|
1999-09-18 04:51:35 +02:00
|
|
|
* begin : top
|
1998-11-09 19:55:33 +01:00
|
|
|
* name1_ = expr1_;
|
1999-09-18 04:51:35 +02:00
|
|
|
* while (cond_) begin : body
|
1998-11-09 19:55:33 +01:00
|
|
|
* statement_;
|
|
|
|
|
* name2_ = expr2_;
|
|
|
|
|
* end
|
|
|
|
|
* end
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const
|
1998-11-09 19:55:33 +01:00
|
|
|
{
|
2014-05-02 05:37:33 +02:00
|
|
|
NetExpr*initial_expr;
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(scope);
|
|
|
|
|
|
1999-05-10 02:16:57 +02:00
|
|
|
const PEIdent*id1 = dynamic_cast<const PEIdent*>(name1_);
|
|
|
|
|
assert(id1);
|
|
|
|
|
|
1999-09-18 04:51:35 +02:00
|
|
|
/* make the expression, and later the initial assignment to
|
|
|
|
|
the condition variable. The statement in the for loop is
|
|
|
|
|
very specifically an assignment. */
|
2001-12-03 05:47:14 +01:00
|
|
|
NetNet*sig = des->find_signal(scope, id1->path());
|
1999-06-06 22:45:38 +02:00
|
|
|
if (sig == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << id1->get_fileline() << ": register ``" << id1->path()
|
2008-02-25 04:40:54 +01:00
|
|
|
<< "'' unknown in " << scope_path(scope) << "." << endl;
|
1999-06-06 22:45:38 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
1998-11-09 19:55:33 +01:00
|
|
|
assert(sig);
|
2001-02-07 22:47:13 +01:00
|
|
|
|
|
|
|
|
/* Make the r-value of the initial assignment, and size it
|
|
|
|
|
properly. Then use it to build the assignment statement. */
|
2014-05-02 05:37:33 +02:00
|
|
|
initial_expr = elaborate_rval_expr(des, scope, sig->net_type(),
|
|
|
|
|
sig->data_type(), sig->vector_width(),
|
|
|
|
|
expr1_);
|
2005-05-17 22:56:55 +02:00
|
|
|
|
2015-04-13 17:31:28 +02:00
|
|
|
if (debug_elaborate && initial_expr) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: FOR initial assign: "
|
2014-05-02 05:37:33 +02:00
|
|
|
<< sig->name() << " = " << *initial_expr << endl;
|
2008-03-25 22:06:16 +01:00
|
|
|
}
|
|
|
|
|
|
1999-09-18 04:51:35 +02:00
|
|
|
/* Elaborate the statement that is contained in the for
|
|
|
|
|
loop. If there is an error, this will return 0 and I should
|
|
|
|
|
skip the append. No need to worry, the error has been
|
|
|
|
|
reported so it's OK that the netlist is bogus. */
|
2015-08-17 22:47:15 +02:00
|
|
|
NetProc*sub;
|
|
|
|
|
if (statement_)
|
|
|
|
|
sub = statement_->elaborate(des, scope);
|
|
|
|
|
else
|
|
|
|
|
sub = new NetBlock(NetBlock::SEQU, 0);
|
1999-09-18 04:51:35 +02:00
|
|
|
|
2012-02-25 18:28:20 +01:00
|
|
|
/* Now elaborate the for_step statement. I really should do
|
|
|
|
|
some error checking here to make sure the step statement
|
|
|
|
|
really does step the variable. */
|
|
|
|
|
NetProc*step = step_->elaborate(des, scope);
|
1999-06-13 18:30:06 +02:00
|
|
|
|
1999-09-18 03:53:08 +02:00
|
|
|
/* Elaborate the condition expression. Try to evaluate it too,
|
|
|
|
|
in case it is a constant. This is an interesting case
|
|
|
|
|
worthy of a warning. */
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*ce = elab_and_eval(des, scope, cond_, -1);
|
2004-03-07 21:04:10 +01:00
|
|
|
if (dynamic_cast<NetEConst*>(ce)) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": warning: condition expression "
|
2004-03-07 21:04:10 +01:00
|
|
|
"of for-loop is constant." << endl;
|
1999-09-18 03:53:08 +02:00
|
|
|
}
|
|
|
|
|
|
2015-04-13 15:39:10 +02:00
|
|
|
/* Error recovery - if we failed to elaborate any of the loop
|
|
|
|
|
expressions, give up now. */
|
|
|
|
|
if (initial_expr == 0 || ce == 0 || step == 0 || sub == 0) {
|
|
|
|
|
delete initial_expr;
|
|
|
|
|
delete ce;
|
|
|
|
|
delete step;
|
|
|
|
|
delete sub;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
1999-09-18 03:53:08 +02:00
|
|
|
/* All done, build up the loop. */
|
|
|
|
|
|
2014-05-02 05:37:33 +02:00
|
|
|
NetForLoop*loop = new NetForLoop(sig, initial_expr, ce, sub, step);
|
2003-10-26 05:49:51 +01:00
|
|
|
loop->set_line(*this);
|
2014-05-02 05:37:33 +02:00
|
|
|
loop->wrap_up();
|
|
|
|
|
return loop;
|
1998-11-09 19:55:33 +01:00
|
|
|
}
|
|
|
|
|
|
1999-09-01 22:46:19 +02:00
|
|
|
/*
|
2000-05-02 02:58:11 +02:00
|
|
|
* (See the PTask::elaborate methods for basic common stuff.)
|
|
|
|
|
*
|
|
|
|
|
* The return value of a function is represented as a reg variable
|
|
|
|
|
* within the scope of the function that has the name of the
|
|
|
|
|
* function. So for example with the function:
|
|
|
|
|
*
|
|
|
|
|
* function [7:0] incr;
|
|
|
|
|
* input [7:0] in1;
|
|
|
|
|
* incr = in1 + 1;
|
|
|
|
|
* endfunction
|
2000-03-08 05:36:53 +01:00
|
|
|
*
|
2000-05-02 02:58:11 +02:00
|
|
|
* The scope of the function is <parent>.incr and there is a reg
|
|
|
|
|
* variable <parent>.incr.incr. The elaborate_1 method is called with
|
|
|
|
|
* the scope of the function, so the return reg is easily located.
|
|
|
|
|
*
|
|
|
|
|
* The function parameters are all inputs, except for the synthetic
|
|
|
|
|
* output parameter that is the return value. The return value goes
|
|
|
|
|
* into port 0, and the parameters are all the remaining ports.
|
1999-09-01 22:46:19 +02:00
|
|
|
*/
|
1999-09-01 00:38:29 +02:00
|
|
|
|
2000-07-30 20:25:43 +02:00
|
|
|
void PFunction::elaborate(Design*des, NetScope*scope) const
|
1999-09-01 22:46:19 +02:00
|
|
|
{
|
2011-04-05 21:43:54 +02:00
|
|
|
if (scope->elab_stage() > 2)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
scope->set_elab_stage(3);
|
|
|
|
|
|
2001-12-03 05:47:14 +01:00
|
|
|
NetFuncDef*def = scope->func_def();
|
2004-06-01 01:34:36 +02:00
|
|
|
if (def == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
2004-06-01 01:34:36 +02:00
|
|
|
<< "No function definition for function "
|
2007-06-02 05:42:12 +02:00
|
|
|
<< scope_path(scope) << endl;
|
2008-02-28 05:54:47 +01:00
|
|
|
des->errors += 1;
|
2004-06-01 01:34:36 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
1999-09-01 22:46:19 +02:00
|
|
|
assert(def);
|
|
|
|
|
|
2011-09-17 21:10:05 +02:00
|
|
|
ivl_assert(*this, statement_);
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc*st = statement_->elaborate(des, scope);
|
1999-09-01 22:46:19 +02:00
|
|
|
if (st == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << statement_->get_fileline() << ": error: Unable to elaborate "
|
2007-06-02 05:42:12 +02:00
|
|
|
"statement in function " << scope->basename() << "." << endl;
|
2011-04-05 21:43:54 +02:00
|
|
|
scope->is_const_func(true); // error recovery
|
1999-09-01 22:46:19 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def->set_proc(st);
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PRelease::elaborate(Design*des, NetScope*scope) const
|
2000-04-22 06:20:19 +02:00
|
|
|
{
|
2000-04-23 05:45:24 +02:00
|
|
|
assert(scope);
|
|
|
|
|
|
2008-11-09 01:26:55 +01:00
|
|
|
if (scope->is_auto() && lval_->has_aa_term(des, scope)) {
|
|
|
|
|
cerr << get_fileline() << ": error: automatically allocated "
|
|
|
|
|
"variables may not be assigned values using procedural "
|
|
|
|
|
"force statements." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-18 20:21:37 +02:00
|
|
|
NetAssign_*lval = lval_->elaborate_lval(des, scope, false, true);
|
2000-04-23 05:45:24 +02:00
|
|
|
if (lval == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
NetRelease*dev = new NetRelease(lval);
|
|
|
|
|
dev->set_line( *this );
|
|
|
|
|
return dev;
|
2000-04-22 06:20:19 +02:00
|
|
|
}
|
|
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PRepeat::elaborate(Design*des, NetScope*scope) const
|
1999-06-19 23:06:16 +02:00
|
|
|
{
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(scope);
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*expr = elab_and_eval(des, scope, expr_, -1);
|
1999-06-19 23:06:16 +02:00
|
|
|
if (expr == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": Unable to elaborate"
|
1999-06-19 23:06:16 +02:00
|
|
|
" repeat expression." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-12-13 20:50:33 +01:00
|
|
|
// If the expression is real, convert to an integer. 64 bits
|
|
|
|
|
// should be more enough for any real use case.
|
|
|
|
|
if (expr->expr_type() == IVL_VT_REAL)
|
|
|
|
|
expr = cast_to_int4(expr, 64);
|
1999-06-19 23:06:16 +02:00
|
|
|
|
2015-08-17 22:47:15 +02:00
|
|
|
NetProc*stat;
|
|
|
|
|
if (statement_)
|
|
|
|
|
stat = statement_->elaborate(des, scope);
|
|
|
|
|
else
|
|
|
|
|
stat = new NetBlock(NetBlock::SEQU, 0);
|
1999-06-19 23:06:16 +02:00
|
|
|
if (stat == 0) return 0;
|
|
|
|
|
|
|
|
|
|
// If the expression is a constant, handle certain special
|
|
|
|
|
// iteration counts.
|
|
|
|
|
if (NetEConst*ce = dynamic_cast<NetEConst*>(expr)) {
|
2008-09-04 01:05:12 +02:00
|
|
|
long val = ce->value().as_long();
|
|
|
|
|
if (val <= 0) {
|
1999-06-19 23:06:16 +02:00
|
|
|
delete expr;
|
|
|
|
|
delete stat;
|
2002-05-27 02:08:45 +02:00
|
|
|
return new NetBlock(NetBlock::SEQU, 0);
|
2008-09-04 01:05:12 +02:00
|
|
|
} else if (val == 1) {
|
1999-06-19 23:06:16 +02:00
|
|
|
delete expr;
|
|
|
|
|
return stat;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetRepeat*proc = new NetRepeat(expr, stat);
|
2011-02-10 06:03:08 +01:00
|
|
|
proc->set_line( *this );
|
1999-06-19 23:06:16 +02:00
|
|
|
return proc;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-24 04:59:57 +02:00
|
|
|
NetProc* PReturn::elaborate(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
NetScope*target = scope;
|
|
|
|
|
for (;;) {
|
|
|
|
|
if (target == 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Return statement is not in a function." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (target->type() == NetScope::FUNC)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (target->type() == NetScope::TASK) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Cannot \"return\" from tasks." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (target->type()==NetScope::BEGIN_END) {
|
|
|
|
|
target = target->parent();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Cannot \"return\" from this scope: " << scope_path(target) << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We don't yet support void functions, so require an
|
|
|
|
|
// expression for the return statement.
|
|
|
|
|
if (expr_ == 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Return from " << scope_path(target)
|
|
|
|
|
<< " requires a return value expression." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNet*res = target->find_signal(target->basename());
|
|
|
|
|
ivl_variable_type_t lv_type = res->data_type();
|
|
|
|
|
unsigned long wid = res->vector_width();
|
|
|
|
|
NetAssign_*lv = new NetAssign_(res);
|
|
|
|
|
|
2013-09-29 23:48:42 +02:00
|
|
|
NetExpr*val = elaborate_rval_expr(des, scope, res->net_type(),
|
|
|
|
|
lv_type, wid, expr_);
|
2013-05-24 04:59:57 +02:00
|
|
|
|
|
|
|
|
NetBlock*proc = new NetBlock(NetBlock::SEQU, 0);
|
|
|
|
|
proc->set_line( *this );
|
|
|
|
|
|
|
|
|
|
NetAssign*assn = new NetAssign(lv, val);
|
|
|
|
|
assn->set_line( *this );
|
|
|
|
|
proc->append(assn);
|
|
|
|
|
|
|
|
|
|
NetDisable*disa = new NetDisable(target);
|
|
|
|
|
disa->set_line( *this );
|
|
|
|
|
proc->append( disa );
|
|
|
|
|
|
|
|
|
|
return proc;
|
|
|
|
|
}
|
|
|
|
|
|
1999-07-03 04:12:51 +02:00
|
|
|
/*
|
|
|
|
|
* A task definition is elaborated by elaborating the statement that
|
1999-07-24 04:11:19 +02:00
|
|
|
* it contains, and connecting its ports to NetNet objects. The
|
|
|
|
|
* netlist doesn't really need the array of parameters once elaboration
|
|
|
|
|
* is complete, but this is the best place to store them.
|
2000-05-02 02:58:11 +02:00
|
|
|
*
|
|
|
|
|
* The first elaboration pass finds the reg objects that match the
|
|
|
|
|
* port names, and creates the NetTaskDef object. The port names are
|
|
|
|
|
* in the form task.port.
|
|
|
|
|
*
|
|
|
|
|
* task foo;
|
|
|
|
|
* output blah;
|
|
|
|
|
* begin <body> end
|
|
|
|
|
* endtask
|
|
|
|
|
*
|
|
|
|
|
* So in the foo example, the PWire objects that represent the ports
|
|
|
|
|
* of the task will include a foo.blah for the blah port. This port is
|
2000-07-30 20:25:43 +02:00
|
|
|
* bound to a NetNet object by looking up the name. All of this is
|
|
|
|
|
* handled by the PTask::elaborate_sig method and the results stashed
|
2003-01-27 06:09:17 +01:00
|
|
|
* in the created NetTaskDef attached to the scope.
|
2000-05-02 02:58:11 +02:00
|
|
|
*
|
|
|
|
|
* Elaboration pass 2 for the task definition causes the statement of
|
|
|
|
|
* the task to be elaborated and attached to the NetTaskDef object
|
|
|
|
|
* created in pass 1.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: I am not sure why I bothered to prepend the task name to the
|
|
|
|
|
* port name when making the port list. It is not really useful, but
|
|
|
|
|
* that is what I did in pform_make_task_ports, so there it is.
|
1999-07-03 04:12:51 +02:00
|
|
|
*/
|
2000-04-28 18:50:53 +02:00
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
void PTask::elaborate(Design*des, NetScope*task) const
|
1999-09-30 23:28:34 +02:00
|
|
|
{
|
2008-03-04 05:49:52 +01:00
|
|
|
// Elaborate any processes that are part of this scope that
|
|
|
|
|
// aren't the definition itself. This can happen, for example,
|
|
|
|
|
// with variable initialization statements in this scope.
|
|
|
|
|
elaborate_behaviors_(des, task);
|
|
|
|
|
|
2001-04-02 04:28:12 +02:00
|
|
|
NetTaskDef*def = task->task_def();
|
1999-09-30 23:28:34 +02:00
|
|
|
assert(def);
|
|
|
|
|
|
1999-09-30 04:43:01 +02:00
|
|
|
NetProc*st;
|
|
|
|
|
if (statement_ == 0) {
|
2002-05-27 02:08:45 +02:00
|
|
|
st = new NetBlock(NetBlock::SEQU, 0);
|
1999-09-30 04:43:01 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
st = statement_->elaborate(des, task);
|
1999-09-30 04:43:01 +02:00
|
|
|
if (st == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << statement_->get_fileline() << ": Unable to elaborate "
|
2007-06-02 05:42:12 +02:00
|
|
|
"statement in task " << scope_path(task)
|
2007-12-20 18:31:01 +01:00
|
|
|
<< " at " << get_fileline() << "." << endl;
|
1999-09-30 04:43:01 +02:00
|
|
|
return;
|
|
|
|
|
}
|
1999-07-03 04:12:51 +02:00
|
|
|
}
|
|
|
|
|
|
1999-09-30 23:28:34 +02:00
|
|
|
def->set_proc(st);
|
1999-07-03 04:12:51 +02:00
|
|
|
}
|
|
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PTrigger::elaborate(Design*des, NetScope*scope) const
|
2000-04-01 21:31:57 +02:00
|
|
|
{
|
2000-04-04 05:20:15 +02:00
|
|
|
assert(scope);
|
|
|
|
|
|
2003-09-20 03:05:35 +02:00
|
|
|
NetNet* sig = 0;
|
|
|
|
|
const NetExpr*par = 0;
|
|
|
|
|
NetEvent* eve = 0;
|
|
|
|
|
|
2008-10-28 18:52:39 +01:00
|
|
|
NetScope*found_in = symbol_search(this, des, scope, event_,
|
2007-01-16 06:44:14 +01:00
|
|
|
sig, par, eve);
|
2003-09-20 03:05:35 +02:00
|
|
|
|
|
|
|
|
if (found_in == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: event <" << event_ << ">"
|
2000-04-04 05:20:15 +02:00
|
|
|
<< " not found." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-20 03:05:35 +02:00
|
|
|
if (eve == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: <" << event_ << ">"
|
2003-09-20 03:05:35 +02:00
|
|
|
<< " is not a named event." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetEvTrig*trig = new NetEvTrig(eve);
|
2000-04-04 05:20:15 +02:00
|
|
|
trig->set_line(*this);
|
|
|
|
|
return trig;
|
2000-04-01 21:31:57 +02:00
|
|
|
}
|
|
|
|
|
|
1998-11-11 04:13:04 +01:00
|
|
|
/*
|
|
|
|
|
* The while loop is fairly directly represented in the netlist.
|
|
|
|
|
*/
|
2001-11-22 07:20:59 +01:00
|
|
|
NetProc* PWhile::elaborate(Design*des, NetScope*scope) const
|
1998-11-11 04:13:04 +01:00
|
|
|
{
|
2015-04-13 15:39:10 +02:00
|
|
|
NetExpr*ce = elab_and_eval(des, scope, cond_, -1);
|
2015-08-17 22:47:15 +02:00
|
|
|
NetProc*sub;
|
|
|
|
|
if (statement_)
|
|
|
|
|
sub = statement_->elaborate(des, scope);
|
|
|
|
|
else
|
|
|
|
|
sub = new NetBlock(NetBlock::SEQU, 0);
|
2015-04-13 15:39:10 +02:00
|
|
|
if (ce == 0 || sub == 0) {
|
|
|
|
|
delete ce;
|
|
|
|
|
delete sub;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
NetWhile*loop = new NetWhile(ce, sub);
|
2011-02-28 04:20:16 +01:00
|
|
|
loop->set_line(*this);
|
1998-11-11 04:13:04 +01:00
|
|
|
return loop;
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-05 06:59:10 +01:00
|
|
|
bool PProcess::elaborate(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
2011-03-30 08:42:26 +02:00
|
|
|
scope->in_final(type() == IVL_PR_FINAL);
|
2007-03-05 06:59:10 +01:00
|
|
|
NetProc*cur = statement_->elaborate(des, scope);
|
2011-03-30 08:42:26 +02:00
|
|
|
scope->in_final(false);
|
2007-03-05 06:59:10 +01:00
|
|
|
if (cur == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-22 07:15:49 +02:00
|
|
|
NetProcTop*top=new NetProcTop(scope, type(), cur);
|
2007-03-05 06:59:10 +01:00
|
|
|
ivl_assert(*this, top);
|
|
|
|
|
|
|
|
|
|
// Evaluate the attributes for this process, if there
|
|
|
|
|
// are any. These attributes are to be attached to the
|
|
|
|
|
// NetProcTop object.
|
2012-11-13 03:13:41 +01:00
|
|
|
struct attrib_list_t*attrib_list;
|
2007-03-05 06:59:10 +01:00
|
|
|
unsigned attrib_list_n = 0;
|
|
|
|
|
attrib_list = evaluate_attributes(attributes, attrib_list_n, des, scope);
|
|
|
|
|
|
|
|
|
|
for (unsigned adx = 0 ; adx < attrib_list_n ; adx += 1)
|
|
|
|
|
top->attribute(attrib_list[adx].key,
|
|
|
|
|
attrib_list[adx].val);
|
|
|
|
|
|
|
|
|
|
delete[]attrib_list;
|
|
|
|
|
|
|
|
|
|
top->set_line(*this);
|
|
|
|
|
des->add_process(top);
|
|
|
|
|
|
|
|
|
|
/* Detect the special case that this is a combinational
|
|
|
|
|
always block. We want to attach an _ivl_schedule_push
|
|
|
|
|
attribute to this process so that it starts up and
|
|
|
|
|
gets into its wait statement before non-combinational
|
|
|
|
|
code is executed. */
|
|
|
|
|
do {
|
2008-10-22 07:15:49 +02:00
|
|
|
if (top->type() != IVL_PR_ALWAYS)
|
2007-03-05 06:59:10 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
NetEvWait*st = dynamic_cast<NetEvWait*>(top->statement());
|
|
|
|
|
if (st == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (st->nevents() != 1)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
NetEvent*ev = st->event(0);
|
|
|
|
|
|
|
|
|
|
if (ev->nprobe() == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
bool anyedge_test = true;
|
|
|
|
|
for (unsigned idx = 0 ; anyedge_test && (idx<ev->nprobe())
|
|
|
|
|
; idx += 1) {
|
|
|
|
|
const NetEvProbe*pr = ev->probe(idx);
|
|
|
|
|
if (pr->edge() != NetEvProbe::ANYEDGE)
|
|
|
|
|
anyedge_test = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! anyedge_test)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
top->attribute(perm_string::literal("_ivl_schedule_push"),
|
|
|
|
|
verinum(1));
|
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-23 06:57:19 +02:00
|
|
|
void PSpecPath::elaborate(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
uint64_t delay_value[12];
|
|
|
|
|
unsigned ndelays = 0;
|
|
|
|
|
|
2006-09-28 06:35:18 +02:00
|
|
|
/* Do not elaborate specify delay paths if this feature is
|
|
|
|
|
turned off. */
|
2009-06-09 22:12:45 +02:00
|
|
|
if (!gn_specify_blocks_flag) return;
|
2006-09-28 06:35:18 +02:00
|
|
|
|
2007-02-12 02:52:21 +01:00
|
|
|
ivl_assert(*this, conditional || (condition==0));
|
|
|
|
|
|
2006-09-23 06:57:19 +02:00
|
|
|
ndelays = delays.size();
|
2009-06-09 22:12:45 +02:00
|
|
|
if (ndelays > 12) ndelays = 12;
|
2006-09-23 06:57:19 +02:00
|
|
|
|
2009-04-14 03:06:17 +02:00
|
|
|
/* Print a warning if we find default and `timescale based
|
|
|
|
|
* delays in the design, since this is likely an error. */
|
|
|
|
|
if (scope->time_from_timescale()) dly_used_timescale = true;
|
|
|
|
|
else dly_used_no_timescale = true;
|
|
|
|
|
|
|
|
|
|
if (display_ts_dly_warning &&
|
|
|
|
|
dly_used_no_timescale && dly_used_timescale) {
|
|
|
|
|
cerr << "warning: Found both default and "
|
|
|
|
|
"`timescale based delays. Use" << endl;
|
|
|
|
|
cerr << " -Wtimescale to find the "
|
|
|
|
|
"module(s) with no `timescale." << endl;
|
|
|
|
|
display_ts_dly_warning = false;
|
|
|
|
|
}
|
2006-10-03 07:06:00 +02:00
|
|
|
|
|
|
|
|
/* Elaborate the delay values themselves. Remember to scale
|
|
|
|
|
them for the timescale/precision of the scope. */
|
2006-09-23 06:57:19 +02:00
|
|
|
for (unsigned idx = 0 ; idx < ndelays ; idx += 1) {
|
|
|
|
|
PExpr*exp = delays[idx];
|
2011-02-26 23:59:52 +01:00
|
|
|
NetExpr*cur = elab_and_eval(des, scope, exp, -1);
|
2006-09-23 06:57:19 +02:00
|
|
|
|
2010-06-19 01:03:17 +02:00
|
|
|
if (NetEConst*con = dynamic_cast<NetEConst*> (cur)) {
|
|
|
|
|
verinum fn = con->value();
|
|
|
|
|
delay_value[idx] = des->scale_to_precision(fn.as_ulong64(),
|
|
|
|
|
scope);
|
2006-10-03 07:06:00 +02:00
|
|
|
|
2010-06-19 01:03:17 +02:00
|
|
|
} else if (NetECReal*rcon = dynamic_cast<NetECReal*>(cur)) {
|
|
|
|
|
delay_value[idx] = get_scaled_time_from_real(des, scope,
|
|
|
|
|
rcon);
|
2006-10-03 07:06:00 +02:00
|
|
|
|
2006-09-23 06:57:19 +02:00
|
|
|
} else {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Path delay value "
|
2010-06-19 01:03:17 +02:00
|
|
|
<< "must be constant (" << *cur << ")." << endl;
|
2006-09-23 06:57:19 +02:00
|
|
|
delay_value[idx] = 0;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
delete cur;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-19 00:18:27 +02:00
|
|
|
switch (delays.size()) {
|
2006-09-23 06:57:19 +02:00
|
|
|
case 1:
|
|
|
|
|
case 2:
|
|
|
|
|
case 3:
|
|
|
|
|
case 6:
|
|
|
|
|
case 12:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Incorrect delay configuration."
|
2008-04-19 00:18:27 +02:00
|
|
|
<< " Given " << delays.size() << " delay expressions." << endl;
|
2006-09-23 06:57:19 +02:00
|
|
|
ndelays = 1;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-01 07:19:38 +01:00
|
|
|
NetNet*condit_sig = 0;
|
2008-04-29 04:00:29 +02:00
|
|
|
if (conditional && condition) {
|
2007-03-01 07:19:38 +01:00
|
|
|
|
|
|
|
|
NetExpr*tmp = elab_and_eval(des, scope, condition, -1);
|
|
|
|
|
ivl_assert(*condition, tmp);
|
|
|
|
|
|
|
|
|
|
// FIXME: Look for constant expressions here?
|
|
|
|
|
|
|
|
|
|
// Get a net form.
|
2008-12-22 22:48:34 +01:00
|
|
|
condit_sig = tmp->synthesize(des, scope, tmp);
|
2007-03-01 07:19:38 +01:00
|
|
|
ivl_assert(*condition, condit_sig);
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-08 03:29:27 +01:00
|
|
|
/* A parallel connection does not support more than a one to one
|
|
|
|
|
connection (source/destination). */
|
2011-11-23 04:38:36 +01:00
|
|
|
if (! full_flag_ && ((src.size() != 1) || (dst.size() != 1))) {
|
2011-11-08 03:29:27 +01:00
|
|
|
/* To be compatible with NC-Verilog we allow a parallel connection
|
|
|
|
|
* with multiple sources/destinations if all the paths are only a
|
|
|
|
|
* single bit wide (a scalar or a one bit vector). */
|
|
|
|
|
bool all_single = true;
|
|
|
|
|
typedef std::vector<perm_string>::const_iterator str_vec_iter;
|
|
|
|
|
for (str_vec_iter cur = src.begin();
|
|
|
|
|
( cur != src.end() && all_single); ++ cur) {
|
|
|
|
|
NetNet *psig = scope->find_signal(*cur);
|
|
|
|
|
/* We will report a missing signal as invalid later. For
|
|
|
|
|
* now assume it's a single bit. */
|
|
|
|
|
if (psig == 0) continue;
|
|
|
|
|
if (psig->vector_width() != 1) all_single = false;
|
|
|
|
|
}
|
|
|
|
|
for (str_vec_iter cur = dst.begin();
|
|
|
|
|
( cur != dst.end() && all_single); ++ cur) {
|
|
|
|
|
NetNet *psig = scope->find_signal(*cur);
|
|
|
|
|
/* The same as above for source paths. */
|
|
|
|
|
if (psig == 0) continue;
|
|
|
|
|
if (psig->vector_width() != 1) all_single = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! all_single) {
|
|
|
|
|
cerr << get_fileline() << ": error: Parallel connections "
|
|
|
|
|
"only support one source/destination path found ("
|
|
|
|
|
<< src.size() << "/" << dst.size() << ")." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-23 06:57:19 +02:00
|
|
|
/* Create all the various paths from the path specifier. */
|
|
|
|
|
typedef std::vector<perm_string>::const_iterator str_vector_iter;
|
|
|
|
|
for (str_vector_iter cur = dst.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur != dst.end() ; ++ cur ) {
|
2006-09-23 06:57:19 +02:00
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Path to " << (*cur);
|
2007-03-01 07:19:38 +01:00
|
|
|
if (condit_sig)
|
|
|
|
|
cerr << " if " << condit_sig->name();
|
2008-04-29 04:00:29 +02:00
|
|
|
else if (conditional)
|
|
|
|
|
cerr << " ifnone";
|
|
|
|
|
cerr << " from ";
|
2006-09-23 06:57:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNet*dst_sig = scope->find_signal(*cur);
|
|
|
|
|
if (dst_sig == 0) {
|
2011-11-08 03:29:27 +01:00
|
|
|
cerr << get_fileline() << ": error: No wire '"
|
|
|
|
|
<< *cur << "' in this module." << endl;
|
2006-09-23 06:57:19 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-06 18:17:20 +01:00
|
|
|
unsigned long dst_wid = dst_sig->vector_width();
|
|
|
|
|
|
2007-03-03 06:56:55 +01:00
|
|
|
if (dst_sig->port_type() != NetNet::POUTPUT
|
|
|
|
|
&& dst_sig->port_type() != NetNet::PINOUT) {
|
|
|
|
|
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Path destination "
|
2007-03-03 06:56:55 +01:00
|
|
|
<< *cur << " must be an output or inout port." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-23 06:57:19 +02:00
|
|
|
NetDelaySrc*path = new NetDelaySrc(scope, scope->local_symbol(),
|
2008-04-29 04:00:29 +02:00
|
|
|
src.size(), condit_sig,
|
|
|
|
|
conditional);
|
2006-09-23 06:57:19 +02:00
|
|
|
path->set_line(*this);
|
|
|
|
|
|
2007-04-13 04:34:35 +02:00
|
|
|
// The presence of the data_source_expression indicates
|
|
|
|
|
// that this is an edge sensitive path. If so, then set
|
|
|
|
|
// the edges. Note that edge==0 is BOTH edges.
|
|
|
|
|
if (data_source_expression) {
|
|
|
|
|
if (edge >= 0) path->set_posedge();
|
|
|
|
|
if (edge <= 0) path->set_negedge();
|
|
|
|
|
}
|
2007-03-02 07:13:22 +01:00
|
|
|
|
2006-09-23 06:57:19 +02:00
|
|
|
switch (ndelays) {
|
|
|
|
|
case 12:
|
|
|
|
|
path->set_delays(delay_value[0], delay_value[1],
|
|
|
|
|
delay_value[2], delay_value[3],
|
|
|
|
|
delay_value[4], delay_value[5],
|
|
|
|
|
delay_value[6], delay_value[7],
|
|
|
|
|
delay_value[8], delay_value[9],
|
|
|
|
|
delay_value[10], delay_value[11]);
|
|
|
|
|
break;
|
|
|
|
|
case 6:
|
|
|
|
|
path->set_delays(delay_value[0], delay_value[1],
|
|
|
|
|
delay_value[2], delay_value[3],
|
|
|
|
|
delay_value[4], delay_value[5]);
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
path->set_delays(delay_value[0], delay_value[1],
|
|
|
|
|
delay_value[2]);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
path->set_delays(delay_value[0], delay_value[1]);
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
path->set_delays(delay_value[0]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned idx = 0;
|
|
|
|
|
for (str_vector_iter cur_src = src.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur_src != src.end() ; ++ cur_src ) {
|
2006-09-23 06:57:19 +02:00
|
|
|
NetNet*src_sig = scope->find_signal(*cur_src);
|
2011-11-08 03:29:27 +01:00
|
|
|
if (src_sig == 0) {
|
|
|
|
|
cerr << get_fileline() << ": error: No wire '"
|
|
|
|
|
<< *cur_src << "' in this module." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2006-09-23 06:57:19 +02:00
|
|
|
|
2008-04-29 04:00:29 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
if (cur_src != src.begin()) cerr << " and ";
|
|
|
|
|
cerr << src_sig->name();
|
|
|
|
|
}
|
|
|
|
|
|
2008-01-05 04:21:26 +01:00
|
|
|
if ( (src_sig->port_type() != NetNet::PINPUT)
|
|
|
|
|
&& (src_sig->port_type() != NetNet::PINOUT) ) {
|
2007-03-03 06:56:55 +01:00
|
|
|
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Path source "
|
2007-03-03 06:56:55 +01:00
|
|
|
<< *cur_src << " must be an input or inout port."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-06 18:17:20 +01:00
|
|
|
// For a parallel connection the source and destination
|
|
|
|
|
// must be the same width.
|
2011-11-23 04:38:36 +01:00
|
|
|
if (! full_flag_) {
|
2011-11-06 18:17:20 +01:00
|
|
|
unsigned long src_wid = src_sig->vector_width();
|
|
|
|
|
if (src_wid != dst_wid) {
|
|
|
|
|
cerr << get_fileline() << ": error: For a "
|
|
|
|
|
"parallel connection the "
|
|
|
|
|
"source/destination width must match "
|
|
|
|
|
"found (" << src_wid << "/" << dst_wid
|
|
|
|
|
<< ")." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-23 06:57:19 +02:00
|
|
|
connect(src_sig->pin(0), path->pin(idx));
|
|
|
|
|
idx += 1;
|
|
|
|
|
}
|
2008-04-29 04:00:29 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << endl;
|
|
|
|
|
}
|
2006-09-23 06:57:19 +02:00
|
|
|
|
2007-03-01 07:19:38 +01:00
|
|
|
if (condit_sig)
|
|
|
|
|
connect(condit_sig->pin(0), path->pin(idx));
|
|
|
|
|
|
2006-09-23 06:57:19 +02:00
|
|
|
dst_sig->add_delay_path(path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-19 06:54:58 +02:00
|
|
|
static void elaborate_functions(Design*des, NetScope*scope,
|
|
|
|
|
const map<perm_string,PFunction*>&funcs)
|
|
|
|
|
{
|
|
|
|
|
typedef map<perm_string,PFunction*>::const_iterator mfunc_it_t;
|
|
|
|
|
for (mfunc_it_t cur = funcs.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur != funcs.end() ; ++ cur ) {
|
2008-06-19 06:54:58 +02:00
|
|
|
|
|
|
|
|
hname_t use_name ( (*cur).first );
|
|
|
|
|
NetScope*fscope = scope->child(use_name);
|
|
|
|
|
assert(fscope);
|
|
|
|
|
(*cur).second->elaborate(des, fscope);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void elaborate_tasks(Design*des, NetScope*scope,
|
|
|
|
|
const map<perm_string,PTask*>&tasks)
|
|
|
|
|
{
|
|
|
|
|
typedef map<perm_string,PTask*>::const_iterator mtask_it_t;
|
|
|
|
|
for (mtask_it_t cur = tasks.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur != tasks.end() ; ++ cur ) {
|
2008-06-19 06:54:58 +02:00
|
|
|
|
|
|
|
|
hname_t use_name ( (*cur).first );
|
|
|
|
|
NetScope*tscope = scope->child(use_name);
|
|
|
|
|
assert(tscope);
|
|
|
|
|
(*cur).second->elaborate(des, tscope);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-15 04:08:32 +01:00
|
|
|
static void elaborate_classes(Design*des, NetScope*scope,
|
|
|
|
|
const map<perm_string,PClass*>&classes)
|
|
|
|
|
{
|
|
|
|
|
for (map<perm_string,PClass*>::const_iterator cur = classes.begin()
|
|
|
|
|
; cur != classes.end() ; ++ cur) {
|
|
|
|
|
netclass_t*use_class = scope->find_class(cur->second->pscope_name());
|
|
|
|
|
use_class->elaborate(des, cur->second);
|
2013-06-28 16:57:35 +02:00
|
|
|
|
|
|
|
|
if (use_class->test_for_missing_initializers()) {
|
|
|
|
|
cerr << cur->second->get_fileline() << ": error: "
|
|
|
|
|
<< "Const properties of class " << use_class->get_name()
|
|
|
|
|
<< " are missing initialization." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
2013-03-15 04:08:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-07 02:38:36 +02:00
|
|
|
bool PPackage::elaborate(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
bool result_flag = true;
|
|
|
|
|
|
|
|
|
|
// Elaborate function methods, and...
|
|
|
|
|
elaborate_functions(des, scope, funcs);
|
|
|
|
|
|
|
|
|
|
// Elaborate task methods.
|
|
|
|
|
elaborate_tasks(des, scope, tasks);
|
|
|
|
|
|
2014-10-11 03:53:53 +02:00
|
|
|
// Elaborate class definitions.
|
|
|
|
|
elaborate_classes(des, scope, classes);
|
|
|
|
|
|
2013-04-07 02:38:36 +02:00
|
|
|
return result_flag;
|
|
|
|
|
}
|
|
|
|
|
|
2000-04-04 05:20:15 +02:00
|
|
|
/*
|
|
|
|
|
* When a module is instantiated, it creates the scope then uses this
|
|
|
|
|
* method to elaborate the contents of the module.
|
|
|
|
|
*/
|
2012-06-04 21:43:33 +02:00
|
|
|
|
2000-03-08 05:36:53 +01:00
|
|
|
bool Module::elaborate(Design*des, NetScope*scope) const
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
1999-01-25 06:45:56 +01:00
|
|
|
bool result_flag = true;
|
|
|
|
|
|
2006-04-10 02:37:42 +02:00
|
|
|
// Elaborate within the generate blocks.
|
|
|
|
|
typedef list<PGenerate*>::const_iterator generate_it_t;
|
|
|
|
|
for (generate_it_t cur = generate_schemes.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur != generate_schemes.end() ; ++ cur ) {
|
2007-06-22 04:04:48 +02:00
|
|
|
(*cur)->elaborate(des, scope);
|
2006-04-10 02:37:42 +02:00
|
|
|
}
|
1998-11-04 00:28:49 +01:00
|
|
|
|
1999-08-26 00:22:41 +02:00
|
|
|
// Elaborate functions.
|
2008-06-19 06:54:58 +02:00
|
|
|
elaborate_functions(des, scope, funcs);
|
1999-08-26 00:22:41 +02:00
|
|
|
|
1999-07-03 04:12:51 +02:00
|
|
|
// Elaborate the task definitions. This is done before the
|
|
|
|
|
// behaviors so that task calls may reference these, and after
|
|
|
|
|
// the signals so that the tasks can reference them.
|
2008-06-19 06:54:58 +02:00
|
|
|
elaborate_tasks(des, scope, tasks);
|
1999-07-03 04:12:51 +02:00
|
|
|
|
2014-01-31 01:16:19 +01:00
|
|
|
// Elaborate class definitions.
|
2013-03-15 04:08:32 +01:00
|
|
|
elaborate_classes(des, scope, classes);
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
// Get all the gates of the module and elaborate them by
|
|
|
|
|
// connecting them to the signals. The gate may be simple or
|
|
|
|
|
// complex.
|
|
|
|
|
const list<PGate*>&gl = get_gates();
|
|
|
|
|
|
|
|
|
|
for (list<PGate*>::const_iterator gt = gl.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; gt != gl.end() ; ++ gt ) {
|
1998-11-04 00:28:49 +01:00
|
|
|
|
2001-11-22 07:20:59 +01:00
|
|
|
(*gt)->elaborate(des, scope);
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2002-05-26 03:39:02 +02:00
|
|
|
// Elaborate the behaviors, making processes out of them. This
|
|
|
|
|
// involves scanning the PProcess* list, creating a NetProcTop
|
|
|
|
|
// for each process.
|
2008-03-04 05:49:52 +01:00
|
|
|
result_flag &= elaborate_behaviors_(des, scope);
|
1999-01-25 06:45:56 +01:00
|
|
|
|
2006-09-23 06:57:19 +02:00
|
|
|
// Elaborate the specify paths of the module.
|
|
|
|
|
|
|
|
|
|
for (list<PSpecPath*>::const_iterator sp = specify_paths.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; sp != specify_paths.end() ; ++ sp ) {
|
2006-09-23 06:57:19 +02:00
|
|
|
|
|
|
|
|
(*sp)->elaborate(des, scope);
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-25 06:45:56 +01:00
|
|
|
return result_flag;
|
1998-11-04 00:28:49 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-15 04:08:32 +01:00
|
|
|
/*
|
|
|
|
|
* Elaborating a netclass_t means elaborating the PFunction and PTask
|
|
|
|
|
* objects that it contains. The scopes and signals have already been
|
|
|
|
|
* elaborated in the class of the netclass_t scope, so we can get the
|
|
|
|
|
* child scope for each definition and use that for the context of the
|
|
|
|
|
* function.
|
|
|
|
|
*/
|
|
|
|
|
void netclass_t::elaborate(Design*des, PClass*pclass)
|
|
|
|
|
{
|
2013-07-03 04:41:58 +02:00
|
|
|
if (! pclass->type->initialize_static.empty()) {
|
|
|
|
|
std::vector<Statement*>&stmt_list = pclass->type->initialize_static;
|
|
|
|
|
NetBlock*stmt = new NetBlock(NetBlock::SEQU, 0);
|
|
|
|
|
for (size_t idx = 0 ; idx < stmt_list.size() ; idx += 1) {
|
|
|
|
|
NetProc*tmp = stmt_list[idx]->elaborate(des, class_scope_);
|
|
|
|
|
if (tmp == 0) continue;
|
|
|
|
|
stmt->append(tmp);
|
|
|
|
|
}
|
|
|
|
|
NetProcTop*top = new NetProcTop(class_scope_, IVL_PR_INITIAL, stmt);
|
|
|
|
|
top->set_line(*pclass);
|
|
|
|
|
des->add_process(top);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-15 04:08:32 +01:00
|
|
|
for (map<perm_string,PFunction*>::iterator cur = pclass->funcs.begin()
|
|
|
|
|
; cur != pclass->funcs.end() ; ++ cur) {
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << cur->second->get_fileline() << ": netclass_t::elaborate: "
|
|
|
|
|
<< "Elaborate class " << scope_path(class_scope_)
|
|
|
|
|
<< " function method " << cur->first << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetScope*scope = class_scope_->child( hname_t(cur->first) );
|
|
|
|
|
ivl_assert(*cur->second, scope);
|
|
|
|
|
cur->second->elaborate(des, scope);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (map<perm_string,PTask*>::iterator cur = pclass->tasks.begin()
|
|
|
|
|
; cur != pclass->tasks.end() ; ++ cur) {
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << cur->second->get_fileline() << ": netclass_t::elaborate: "
|
|
|
|
|
<< "Elaborate class " << scope_path(class_scope_)
|
|
|
|
|
<< " task method " << cur->first << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetScope*scope = class_scope_->child( hname_t(cur->first) );
|
|
|
|
|
ivl_assert(*cur->second, scope);
|
|
|
|
|
cur->second->elaborate(des, scope);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-22 04:04:48 +02:00
|
|
|
bool PGenerate::elaborate(Design*des, NetScope*container) const
|
2006-04-10 02:37:42 +02:00
|
|
|
{
|
2008-11-28 04:45:22 +01:00
|
|
|
if (direct_nested_)
|
|
|
|
|
return elaborate_direct_(des, container);
|
|
|
|
|
|
2006-04-10 02:37:42 +02:00
|
|
|
bool flag = true;
|
|
|
|
|
|
2012-05-01 01:00:25 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PGenerate::elaborate: "
|
|
|
|
|
"generate " << scheme_type
|
|
|
|
|
<< " elaborating in scope " << scope_path(container)
|
|
|
|
|
<< "." << endl;
|
2015-09-30 02:44:28 +02:00
|
|
|
cerr << get_fileline() << ": PGenerate::elaborate: "
|
|
|
|
|
"generate scope_name=" << scope_name
|
|
|
|
|
<< ", id_number=" << id_number << endl;
|
2012-05-01 01:00:25 +02:00
|
|
|
}
|
|
|
|
|
|
2008-02-10 07:19:42 +01:00
|
|
|
// Handle the special case that this is a CASE scheme. In this
|
|
|
|
|
// case the PGenerate itself does not have the generated
|
|
|
|
|
// item. Look instead for the case ITEM that has a scope
|
|
|
|
|
// generated for it.
|
|
|
|
|
if (scheme_type == PGenerate::GS_CASE) {
|
|
|
|
|
|
|
|
|
|
typedef list<PGenerate*>::const_iterator generate_it_t;
|
2008-06-18 06:45:37 +02:00
|
|
|
for (generate_it_t cur = generate_schemes.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur != generate_schemes.end() ; ++ cur ) {
|
2008-02-10 07:19:42 +01:00
|
|
|
PGenerate*item = *cur;
|
2008-11-28 04:45:22 +01:00
|
|
|
if (item->direct_nested_ || !item->scope_list_.empty()) {
|
2008-02-10 07:19:42 +01:00
|
|
|
flag &= item->elaborate(des, container);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-10 02:37:42 +02:00
|
|
|
typedef list<NetScope*>::const_iterator scope_list_it_t;
|
|
|
|
|
for (scope_list_it_t cur = scope_list_.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur != scope_list_.end() ; ++ cur ) {
|
2006-04-10 02:37:42 +02:00
|
|
|
|
2007-06-22 04:04:48 +02:00
|
|
|
NetScope*scope = *cur;
|
|
|
|
|
// Check that this scope is one that is contained in the
|
|
|
|
|
// container that the caller passed in.
|
|
|
|
|
if (scope->parent() != container)
|
|
|
|
|
continue;
|
|
|
|
|
|
2007-11-17 19:17:23 +01:00
|
|
|
// If this was an unnamed generate block, replace its
|
|
|
|
|
// temporary name with a name generated using the naming
|
|
|
|
|
// scheme defined in the Verilog-2005 standard.
|
|
|
|
|
const char*name = scope_name.str();
|
|
|
|
|
if (name[0] == '$') {
|
2015-09-30 02:44:28 +02:00
|
|
|
|
2007-11-17 19:17:23 +01:00
|
|
|
if (!scope->auto_name("genblk", '0', name + 4)) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": warning: Couldn't build"
|
2007-11-17 19:17:23 +01:00
|
|
|
<< " unique name for unnamed generate block"
|
|
|
|
|
<< " - using internal name " << name << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-04-10 02:37:42 +02:00
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Elaborate in "
|
2007-06-22 04:04:48 +02:00
|
|
|
<< "scope " << scope_path(scope) << endl;
|
2006-04-10 02:37:42 +02:00
|
|
|
|
2007-06-22 04:04:48 +02:00
|
|
|
flag = elaborate_(des, scope) & flag;
|
2006-04-10 02:37:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-28 04:45:22 +01:00
|
|
|
bool PGenerate::elaborate_direct_(Design*des, NetScope*container) const
|
|
|
|
|
{
|
2012-05-01 01:00:25 +02:00
|
|
|
bool flag = true;
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
2008-11-28 04:45:22 +01:00
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Direct nesting elaborate in scope "
|
2012-05-01 01:00:25 +02:00
|
|
|
<< scope_path(container)
|
|
|
|
|
<< ", scheme_type=" << scheme_type << endl;
|
|
|
|
|
}
|
2008-11-28 04:45:22 +01:00
|
|
|
|
|
|
|
|
// Elaborate for a direct nested generated scheme knows
|
|
|
|
|
// that there are only sub_schemes to be elaborated. There
|
|
|
|
|
// should be exactly 1 active generate scheme, search for it
|
|
|
|
|
// using this loop.
|
|
|
|
|
typedef list<PGenerate*>::const_iterator generate_it_t;
|
|
|
|
|
for (generate_it_t cur = generate_schemes.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur != generate_schemes.end() ; ++ cur ) {
|
2008-11-28 04:45:22 +01:00
|
|
|
PGenerate*item = *cur;
|
2012-05-01 01:00:25 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PGenerate::elaborate_direct_: "
|
|
|
|
|
<< "item->scope_name=" << item->scope_name
|
|
|
|
|
<< ", item->scheme_type=" << item->scheme_type
|
|
|
|
|
<< ", item->direct_nested_=" << item->direct_nested_
|
|
|
|
|
<< ", item->scope_list_.size()=" << item->scope_list_.size()
|
|
|
|
|
<< "." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Special case: If this is a case generate scheme, then
|
2014-01-31 01:16:19 +01:00
|
|
|
// the PGenerate object (item) does not actually
|
2012-05-01 01:00:25 +02:00
|
|
|
// contain anything. Instead scan the case items, which
|
|
|
|
|
// are listed as sub-schemes of the item.
|
|
|
|
|
if (item->scheme_type == PGenerate::GS_CASE) {
|
|
|
|
|
for (generate_it_t icur = item->generate_schemes.begin()
|
|
|
|
|
; icur != item->generate_schemes.end() ; ++ icur ) {
|
|
|
|
|
PGenerate*case_item = *icur;
|
|
|
|
|
if (case_item->direct_nested_ || !case_item->scope_list_.empty()) {
|
|
|
|
|
flag &= case_item->elaborate(des, container);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (item->direct_nested_ || !item->scope_list_.empty()) {
|
|
|
|
|
// Found the item, and it is direct nested.
|
|
|
|
|
flag &= item->elaborate(des, container);
|
|
|
|
|
}
|
2008-11-28 04:45:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-10 02:37:42 +02:00
|
|
|
bool PGenerate::elaborate_(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
2008-06-19 06:54:58 +02:00
|
|
|
elaborate_functions(des, scope, funcs);
|
|
|
|
|
elaborate_tasks(des, scope, tasks);
|
|
|
|
|
|
2006-04-10 02:37:42 +02:00
|
|
|
typedef list<PGate*>::const_iterator gates_it_t;
|
2010-10-23 23:57:59 +02:00
|
|
|
for (gates_it_t cur = gates.begin() ; cur != gates.end() ; ++ cur )
|
2007-03-05 06:59:10 +01:00
|
|
|
(*cur)->elaborate(des, scope);
|
2006-04-10 02:37:42 +02:00
|
|
|
|
2007-03-05 06:59:10 +01:00
|
|
|
typedef list<PProcess*>::const_iterator proc_it_t;
|
2010-10-23 23:57:59 +02:00
|
|
|
for (proc_it_t cur = behaviors.begin(); cur != behaviors.end(); ++ cur )
|
2006-04-10 02:37:42 +02:00
|
|
|
(*cur)->elaborate(des, scope);
|
|
|
|
|
|
2007-10-28 13:07:25 +01:00
|
|
|
typedef list<PGenerate*>::const_iterator generate_it_t;
|
2008-06-18 06:45:37 +02:00
|
|
|
for (generate_it_t cur = generate_schemes.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur != generate_schemes.end() ; ++ cur ) {
|
2007-10-28 13:07:25 +01:00
|
|
|
(*cur)->elaborate(des, scope);
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-10 02:37:42 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-04 05:49:52 +01:00
|
|
|
bool PScope::elaborate_behaviors_(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
bool result_flag = true;
|
|
|
|
|
|
|
|
|
|
// Elaborate the behaviors, making processes out of them. This
|
|
|
|
|
// involves scanning the PProcess* list, creating a NetProcTop
|
|
|
|
|
// for each process.
|
|
|
|
|
for (list<PProcess*>::const_iterator st = behaviors.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; st != behaviors.end() ; ++ st ) {
|
2008-03-04 05:49:52 +01:00
|
|
|
|
|
|
|
|
result_flag &= (*st)->elaborate(des, scope);
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-22 07:15:49 +02:00
|
|
|
for (list<AProcess*>::const_iterator st = analog_behaviors.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; st != analog_behaviors.end() ; ++ st ) {
|
2008-10-22 07:15:49 +02:00
|
|
|
|
|
|
|
|
result_flag &= (*st)->elaborate(des, scope);
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-04 05:49:52 +01:00
|
|
|
return result_flag;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-17 23:42:07 +01:00
|
|
|
class elaborate_package_t : public elaborator_work_item_t {
|
|
|
|
|
public:
|
|
|
|
|
elaborate_package_t(Design*d, NetScope*scope, PPackage*p)
|
|
|
|
|
: elaborator_work_item_t(d), scope_(scope), package_(p)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
~elaborate_package_t() { }
|
|
|
|
|
|
|
|
|
|
virtual void elaborate_runrun()
|
|
|
|
|
{
|
|
|
|
|
if (! package_->elaborate_scope(des, scope_))
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
NetScope*scope_;
|
|
|
|
|
PPackage*package_;
|
|
|
|
|
};
|
|
|
|
|
|
2008-06-25 05:28:08 +02:00
|
|
|
class elaborate_root_scope_t : public elaborator_work_item_t {
|
|
|
|
|
public:
|
2008-10-13 18:51:05 +02:00
|
|
|
elaborate_root_scope_t(Design*des__, NetScope*scope, Module*rmod)
|
|
|
|
|
: elaborator_work_item_t(des__), scope_(scope), rmod_(rmod)
|
2008-06-25 05:28:08 +02:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
~elaborate_root_scope_t() { }
|
|
|
|
|
|
|
|
|
|
virtual void elaborate_runrun()
|
|
|
|
|
{
|
2009-08-06 23:42:13 +02:00
|
|
|
Module::replace_t root_repl;
|
|
|
|
|
for (list<Module::named_expr_t>::iterator cur = Module::user_defparms.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur != Module::user_defparms.end() ; ++ cur ) {
|
2009-08-06 23:42:13 +02:00
|
|
|
|
|
|
|
|
pform_name_t tmp_name = cur->first;
|
|
|
|
|
if (peek_head_name(tmp_name) != scope_->basename())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
tmp_name.pop_front();
|
|
|
|
|
if (tmp_name.size() != 1)
|
|
|
|
|
continue;
|
|
|
|
|
|
2010-12-05 22:28:17 +01:00
|
|
|
root_repl[peek_head_name(tmp_name)] = cur->second;
|
2009-08-06 23:42:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! rmod_->elaborate_scope(des, scope_, root_repl))
|
2008-06-25 05:28:08 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
NetScope*scope_;
|
|
|
|
|
Module*rmod_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class top_defparams : public elaborator_work_item_t {
|
|
|
|
|
public:
|
2015-10-22 11:45:31 +02:00
|
|
|
explicit top_defparams(Design*des__)
|
2008-10-13 18:51:05 +02:00
|
|
|
: elaborator_work_item_t(des__)
|
2008-06-25 05:28:08 +02:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
~top_defparams() { }
|
|
|
|
|
|
|
|
|
|
virtual void elaborate_runrun()
|
|
|
|
|
{
|
2012-05-18 17:07:27 +02:00
|
|
|
if (debug_scopes) {
|
|
|
|
|
cerr << "debug: top_defparams::elaborate_runrun()" << endl;
|
|
|
|
|
}
|
2008-06-25 05:28:08 +02:00
|
|
|
// This method recurses through the scopes, looking for
|
|
|
|
|
// defparam assignments to apply to the parameters in the
|
|
|
|
|
// various scopes. This needs to be done after all the scopes
|
|
|
|
|
// and basic parameters are taken care of because the defparam
|
|
|
|
|
// can assign to a parameter declared *after* it.
|
|
|
|
|
des->run_defparams();
|
|
|
|
|
|
|
|
|
|
// At this point, all parameter overrides are done. Scan the
|
|
|
|
|
// scopes and evaluate the parameters all the way down to
|
|
|
|
|
// constants.
|
|
|
|
|
des->evaluate_parameters();
|
2012-05-18 17:07:27 +02:00
|
|
|
|
|
|
|
|
if (debug_scopes) {
|
|
|
|
|
cerr << "debug: top_defparams::elaborate_runrun() done" << endl;
|
|
|
|
|
}
|
2008-06-25 05:28:08 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2008-06-30 03:46:46 +02:00
|
|
|
class later_defparams : public elaborator_work_item_t {
|
|
|
|
|
public:
|
2015-10-22 11:45:31 +02:00
|
|
|
explicit later_defparams(Design*des__)
|
2008-10-13 18:51:05 +02:00
|
|
|
: elaborator_work_item_t(des__)
|
2008-06-30 03:46:46 +02:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
~later_defparams() { }
|
|
|
|
|
|
|
|
|
|
virtual void elaborate_runrun()
|
|
|
|
|
{
|
2012-05-18 17:07:27 +02:00
|
|
|
if (debug_scopes) {
|
|
|
|
|
cerr << "debug: later_defparams::elaborate_runrun()" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-30 03:46:46 +02:00
|
|
|
list<NetScope*>tmp_list;
|
|
|
|
|
for (set<NetScope*>::iterator cur = des->defparams_later.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; cur != des->defparams_later.end() ; ++ cur )
|
2008-06-30 03:46:46 +02:00
|
|
|
tmp_list.push_back(*cur);
|
|
|
|
|
|
|
|
|
|
des->defparams_later.clear();
|
|
|
|
|
|
|
|
|
|
while (! tmp_list.empty()) {
|
|
|
|
|
NetScope*cur = tmp_list.front();
|
|
|
|
|
tmp_list.pop_front();
|
|
|
|
|
cur->run_defparams_later(des);
|
|
|
|
|
}
|
2012-05-18 17:07:27 +02:00
|
|
|
|
|
|
|
|
// The overridden parameters will be evaluated later in
|
|
|
|
|
// a top_defparams work item.
|
|
|
|
|
|
|
|
|
|
if (debug_scopes) {
|
|
|
|
|
cerr << "debuf: later_defparams::elaborate_runrun() done" << endl;
|
|
|
|
|
}
|
2008-06-30 03:46:46 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2011-03-30 08:42:26 +02:00
|
|
|
bool Design::check_proc_delay() const
|
2008-08-29 00:05:19 +02:00
|
|
|
{
|
|
|
|
|
bool result_flag = true;
|
|
|
|
|
|
|
|
|
|
for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) {
|
|
|
|
|
/* If this is an always block and we have no or zero delay then
|
|
|
|
|
* a runtime infinite loop will happen. If we possible have some
|
|
|
|
|
* delay then print a warning that an infinite loop is possible.
|
|
|
|
|
*/
|
2008-10-22 07:15:49 +02:00
|
|
|
if (pr->type() == IVL_PR_ALWAYS) {
|
2008-08-29 00:05:19 +02:00
|
|
|
DelayType dly_type = pr->statement()->delay_type();
|
|
|
|
|
|
|
|
|
|
if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) {
|
|
|
|
|
cerr << pr->get_fileline() << ": error: always"
|
|
|
|
|
<< " statement does not have any delay." << endl;
|
|
|
|
|
cerr << pr->get_fileline() << ": : A runtime"
|
|
|
|
|
<< " infinite loop will occur." << endl;
|
|
|
|
|
result_flag = false;
|
|
|
|
|
|
|
|
|
|
} else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) {
|
|
|
|
|
cerr << pr->get_fileline() << ": warning: always"
|
|
|
|
|
<< " statement may not have any delay." << endl;
|
|
|
|
|
cerr << pr->get_fileline() << ": : A runtime"
|
|
|
|
|
<< " infinite loop may be possible." << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-30 08:42:26 +02:00
|
|
|
|
|
|
|
|
/* If this is a final block it must not have a delay,
|
|
|
|
|
but this should have been caught by the statement
|
|
|
|
|
elaboration, so maybe this should be an internal
|
|
|
|
|
error? */
|
|
|
|
|
if (pr->type() == IVL_PR_FINAL) {
|
|
|
|
|
DelayType dly_type = pr->statement()->delay_type();
|
|
|
|
|
|
|
|
|
|
if (dly_type != NO_DELAY && dly_type != ZERO_DELAY) {
|
|
|
|
|
cerr << pr->get_fileline() << ": error: final"
|
|
|
|
|
<< " statement contains a delay." << endl;
|
|
|
|
|
result_flag = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-08-29 00:05:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result_flag;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-16 02:33:56 +02:00
|
|
|
void Design::root_elaborate(void)
|
|
|
|
|
{
|
|
|
|
|
for (map<perm_string,netclass_t*>::const_iterator cur = classes_.begin()
|
|
|
|
|
; cur != classes_.end() ; ++ cur) {
|
|
|
|
|
netclass_t*cur_class = cur->second;
|
|
|
|
|
PClass*cur_pclass = class_to_pclass_[cur_class];
|
|
|
|
|
cur_class->elaborate(this, cur_pclass);
|
|
|
|
|
}
|
2014-10-01 01:06:32 +02:00
|
|
|
|
|
|
|
|
for (map<NetScope*,PTaskFunc*>::iterator cur = root_tasks_.begin()
|
|
|
|
|
; cur != root_tasks_.end() ; ++ cur) {
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << cur->second->get_fileline() << ": Design::root_elaborate: "
|
|
|
|
|
<< "Elaborate for root task/func " << scope_path(cur->first) << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cur->second->elaborate(this, cur->first);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-16 02:33:56 +02:00
|
|
|
}
|
|
|
|
|
|
2008-06-22 03:36:46 +02:00
|
|
|
/*
|
|
|
|
|
* This function is the root of all elaboration. The input is the list
|
|
|
|
|
* of root module names. The function locates the Module definitions
|
|
|
|
|
* for each root, does the whole elaboration sequence, and fills in
|
|
|
|
|
* the resulting Design.
|
|
|
|
|
*/
|
2013-04-07 02:38:36 +02:00
|
|
|
|
|
|
|
|
struct pack_elem {
|
|
|
|
|
PPackage*pack;
|
|
|
|
|
NetScope*scope;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct root_elem {
|
|
|
|
|
Module *mod;
|
|
|
|
|
NetScope *scope;
|
|
|
|
|
};
|
|
|
|
|
|
2004-02-18 18:11:54 +01:00
|
|
|
Design* elaborate(list<perm_string>roots)
|
1998-11-04 00:28:49 +01:00
|
|
|
{
|
2013-04-07 02:38:36 +02:00
|
|
|
vector<struct root_elem> root_elems(roots.size());
|
|
|
|
|
vector<struct pack_elem> pack_elems(pform_packages.size());
|
2001-10-19 23:53:24 +02:00
|
|
|
bool rc = true;
|
|
|
|
|
unsigned i = 0;
|
1998-12-01 01:42:13 +01:00
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
// This is the output design. I fill it in as I scan the root
|
|
|
|
|
// module and elaborate what I find.
|
|
|
|
|
Design*des = new Design;
|
|
|
|
|
|
2013-12-15 22:55:26 +01:00
|
|
|
// Elaborate enum sets in $root scope.
|
|
|
|
|
elaborate_rootscope_enumerations(des);
|
|
|
|
|
|
2014-10-01 01:06:32 +02:00
|
|
|
// Elaborate tasks and functions in $root scope.
|
|
|
|
|
elaborate_rootscope_tasks(des);
|
|
|
|
|
|
2014-09-15 05:01:14 +02:00
|
|
|
// Elaborate classes in $root scope.
|
|
|
|
|
elaborate_rootscope_classes(des);
|
|
|
|
|
|
2013-02-17 23:42:07 +01:00
|
|
|
// Elaborate the packages. Package elaboration is simpler
|
|
|
|
|
// because there are fewer sub-scopes involved.
|
2013-04-07 02:38:36 +02:00
|
|
|
i = 0;
|
2013-02-17 23:42:07 +01:00
|
|
|
for (map<perm_string,PPackage*>::iterator pac = pform_packages.begin()
|
|
|
|
|
; pac != pform_packages.end() ; ++ pac) {
|
|
|
|
|
|
2014-10-11 03:53:53 +02:00
|
|
|
ivl_assert(*pac->second, pac->first == pac->second->pscope_name());
|
2013-02-17 23:42:07 +01:00
|
|
|
NetScope*scope = des->make_package_scope(pac->first);
|
|
|
|
|
scope->set_line(pac->second);
|
|
|
|
|
|
|
|
|
|
elaborator_work_item_t*es = new elaborate_package_t(des, scope, pac->second);
|
|
|
|
|
des->elaboration_work_list.push_back(es);
|
2013-04-07 02:38:36 +02:00
|
|
|
|
|
|
|
|
pack_elems[i].pack = pac->second;
|
|
|
|
|
pack_elems[i].scope = scope;
|
|
|
|
|
i += 1;
|
2013-02-17 23:42:07 +01:00
|
|
|
}
|
|
|
|
|
|
2008-06-22 03:36:46 +02:00
|
|
|
// Scan the root modules by name, and elaborate their scopes.
|
2013-04-07 02:38:36 +02:00
|
|
|
i = 0;
|
2004-02-18 18:11:54 +01:00
|
|
|
for (list<perm_string>::const_iterator root = roots.begin()
|
2010-10-23 23:57:59 +02:00
|
|
|
; root != roots.end() ; ++ root ) {
|
2000-03-08 05:36:53 +01:00
|
|
|
|
2001-10-21 01:02:39 +02:00
|
|
|
// Look for the root module in the list.
|
2004-02-18 18:11:54 +01:00
|
|
|
map<perm_string,Module*>::const_iterator mod = pform_modules.find(*root);
|
2001-10-21 01:02:39 +02:00
|
|
|
if (mod == pform_modules.end()) {
|
2001-10-23 01:26:37 +02:00
|
|
|
cerr << "error: Unable to find the root module \""
|
2004-10-04 03:10:51 +02:00
|
|
|
<< (*root) << "\" in the Verilog source." << endl;
|
2001-10-23 01:26:37 +02:00
|
|
|
cerr << " : Perhaps ``-s " << (*root)
|
|
|
|
|
<< "'' is incorrect?" << endl;
|
2001-10-19 23:53:24 +02:00
|
|
|
des->errors++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-10 02:37:42 +02:00
|
|
|
// Get the module definition for this root instance.
|
2001-10-19 23:53:24 +02:00
|
|
|
Module *rmod = (*mod).second;
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2008-08-28 18:01:43 +02:00
|
|
|
// Make the root scope. This makes a NetScope object and
|
2008-06-22 03:36:46 +02:00
|
|
|
// pushes it into the list of root scopes in the Design.
|
2014-12-20 00:10:14 +01:00
|
|
|
NetScope*scope = des->make_root_scope(*root, rmod->program_block,
|
|
|
|
|
rmod->is_interface);
|
2008-06-22 03:36:46 +02:00
|
|
|
|
|
|
|
|
// Collect some basic properties of this scope from the
|
|
|
|
|
// Module definition.
|
2008-04-30 03:58:25 +02:00
|
|
|
scope->set_line(rmod);
|
2001-10-19 23:53:24 +02:00
|
|
|
scope->time_unit(rmod->time_unit);
|
|
|
|
|
scope->time_precision(rmod->time_precision);
|
2009-04-14 03:06:17 +02:00
|
|
|
scope->time_from_timescale(rmod->time_from_timescale);
|
2001-10-19 23:53:24 +02:00
|
|
|
des->set_precision(rmod->time_precision);
|
2006-03-30 03:49:07 +02:00
|
|
|
|
2001-10-19 23:53:24 +02:00
|
|
|
|
2008-08-28 18:01:43 +02:00
|
|
|
// Save this scope, along with its definition, in the
|
2008-06-22 03:36:46 +02:00
|
|
|
// "root_elems" list for later passes.
|
2013-04-07 02:38:36 +02:00
|
|
|
root_elems[i].mod = rmod;
|
|
|
|
|
root_elems[i].scope = scope;
|
|
|
|
|
i += 1;
|
2000-03-08 05:36:53 +01:00
|
|
|
|
2008-06-25 05:28:08 +02:00
|
|
|
// Arrange for these scopes to be elaborated as root
|
|
|
|
|
// scopes. Create an "elaborate_root_scope" object to
|
|
|
|
|
// contain the work item, and append it to the scope
|
|
|
|
|
// elaborations work list.
|
|
|
|
|
elaborator_work_item_t*es = new elaborate_root_scope_t(des, scope, rmod);
|
|
|
|
|
des->elaboration_work_list.push_back(es);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run the work list of scope elaborations until the list is
|
|
|
|
|
// empty. This list is initially populated above where the
|
|
|
|
|
// initial root scopes are primed.
|
|
|
|
|
while (! des->elaboration_work_list.empty()) {
|
2009-12-06 16:56:02 +01:00
|
|
|
// Push a work item to process the defparams of any scopes
|
|
|
|
|
// that are elaborated during this pass. For the first pass
|
|
|
|
|
// this will be all the root scopes. For subsequent passes
|
|
|
|
|
// it will be any scopes created during the previous pass
|
|
|
|
|
// by a generate construct or instance array.
|
|
|
|
|
des->elaboration_work_list.push_back(new top_defparams(des));
|
|
|
|
|
|
2008-06-30 03:46:46 +02:00
|
|
|
// Transfer the queue to a temporary queue.
|
|
|
|
|
list<elaborator_work_item_t*> cur_queue;
|
|
|
|
|
while (! des->elaboration_work_list.empty()) {
|
|
|
|
|
cur_queue.push_back(des->elaboration_work_list.front());
|
|
|
|
|
des->elaboration_work_list.pop_front();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run from the temporary queue. If the temporary queue
|
|
|
|
|
// items create new work queue items, they will show up
|
|
|
|
|
// in the elaboration_work_list and then we get to run
|
|
|
|
|
// through them in the next pass.
|
|
|
|
|
while (! cur_queue.empty()) {
|
|
|
|
|
elaborator_work_item_t*tmp = cur_queue.front();
|
|
|
|
|
cur_queue.pop_front();
|
|
|
|
|
tmp->elaborate_runrun();
|
|
|
|
|
delete tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! des->elaboration_work_list.empty()) {
|
|
|
|
|
des->elaboration_work_list.push_back(new later_defparams(des));
|
|
|
|
|
}
|
2008-06-22 03:36:46 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-16 02:33:56 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << "<toplevel>: elaborate: "
|
|
|
|
|
<< "elaboration work list done. Start processing residual defparams." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-30 03:46:46 +02:00
|
|
|
// Look for residual defparams (that point to a non-existent
|
|
|
|
|
// scope) and clean them out.
|
|
|
|
|
des->residual_defparams();
|
|
|
|
|
|
2001-10-31 04:22:31 +01:00
|
|
|
// Errors already? Probably missing root modules. Just give up
|
|
|
|
|
// now and return nothing.
|
2003-09-25 02:25:14 +02:00
|
|
|
if (des->errors > 0)
|
|
|
|
|
return des;
|
2001-10-31 04:22:31 +01:00
|
|
|
|
2014-09-16 02:33:56 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << "<toplevel>: elaborate: "
|
|
|
|
|
<< "Start calling Package elaborate_sig methods." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2001-10-22 04:05:20 +02:00
|
|
|
// With the parameters evaluated down to constants, we have
|
|
|
|
|
// what we need to elaborate signals and memories. This pass
|
|
|
|
|
// creates all the NetNet and NetMemory objects for declared
|
|
|
|
|
// objects.
|
2013-04-07 02:38:36 +02:00
|
|
|
for (i = 0; i < pack_elems.size(); i += 1) {
|
|
|
|
|
PPackage*pack = pack_elems[i].pack;
|
|
|
|
|
NetScope*scope= pack_elems[i].scope;
|
|
|
|
|
|
|
|
|
|
if (! pack->elaborate_sig(des, scope)) {
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << "<toplevel>" << ": debug: " << pack->pscope_name()
|
|
|
|
|
<< ": elaborate_sig failed!!!" << endl;
|
|
|
|
|
}
|
|
|
|
|
delete des;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-06-04 21:43:33 +02:00
|
|
|
|
2014-09-16 02:33:56 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << "<toplevel>: elaborate: "
|
|
|
|
|
<< "Start calling $root elaborate_sig methods." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
des->root_elaborate_sig();
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << "<toplevel>: elaborate: "
|
|
|
|
|
<< "Start calling root module elaborate_sig methods." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-07 02:38:36 +02:00
|
|
|
for (i = 0; i < root_elems.size(); i++) {
|
|
|
|
|
Module *rmod = root_elems[i].mod;
|
|
|
|
|
NetScope *scope = root_elems[i].scope;
|
2012-06-04 21:43:33 +02:00
|
|
|
scope->set_num_ports( rmod->port_count() );
|
|
|
|
|
|
2013-04-07 02:38:36 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << "<toplevel>" << ": debug: " << rmod->mod_name()
|
|
|
|
|
<< ": port elaboration root "
|
|
|
|
|
<< rmod->port_count() << " ports" << endl;
|
|
|
|
|
}
|
2000-03-10 07:20:48 +01:00
|
|
|
|
2001-10-19 23:53:24 +02:00
|
|
|
if (! rmod->elaborate_sig(des, scope)) {
|
2013-04-07 02:38:36 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << "<toplevel>" << ": debug: " << rmod->mod_name()
|
|
|
|
|
<< ": elaborate_sig failed!!!" << endl;
|
|
|
|
|
}
|
2001-10-19 23:53:24 +02:00
|
|
|
delete des;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-03-21 22:29:20 +01:00
|
|
|
|
|
|
|
|
// Some of the generators need to have the ports correctly
|
|
|
|
|
// defined for the root modules. This code does that.
|
|
|
|
|
for (unsigned idx = 0; idx < rmod->port_count(); idx += 1) {
|
|
|
|
|
vector<PEIdent*> mport = rmod->get_port(idx);
|
2012-06-04 21:43:33 +02:00
|
|
|
unsigned int prt_vector_width = 0;
|
|
|
|
|
PortType::Enum ptype = PortType::PIMPLICIT;
|
2011-03-21 22:29:20 +01:00
|
|
|
for (unsigned pin = 0; pin < mport.size(); pin += 1) {
|
2011-04-15 23:44:05 +02:00
|
|
|
// This really does more than we need and adds extra
|
2011-03-21 22:29:20 +01:00
|
|
|
// stuff to the design that should be cleaned later.
|
2012-06-04 21:43:33 +02:00
|
|
|
NetNet *netnet = mport[pin]->elaborate_subport(des, scope);
|
2012-08-08 22:33:27 +02:00
|
|
|
if (netnet != 0) {
|
2014-03-23 04:50:47 +01:00
|
|
|
// Elaboration may actually fail with
|
|
|
|
|
// erroneous input source
|
|
|
|
|
ivl_assert(*mport[pin], netnet->pin_count()==1);
|
2012-06-04 21:43:33 +02:00
|
|
|
prt_vector_width += netnet->vector_width();
|
|
|
|
|
ptype = PortType::merged(netnet->port_type(), ptype);
|
|
|
|
|
}
|
2011-03-21 22:29:20 +01:00
|
|
|
}
|
2012-06-04 21:43:33 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << "<toplevel>" << ": debug: " << rmod->mod_name()
|
|
|
|
|
<< ": adding module port "
|
|
|
|
|
<< rmod->get_port_name(idx) << endl;
|
|
|
|
|
}
|
|
|
|
|
scope->add_module_port_info(idx, rmod->get_port_name(idx), ptype, prt_vector_width );
|
2011-03-21 22:29:20 +01:00
|
|
|
}
|
2001-10-22 04:05:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now that the structure and parameters are taken care of,
|
|
|
|
|
// run through the pform again and generate the full netlist.
|
|
|
|
|
|
2013-04-07 02:38:36 +02:00
|
|
|
for (i = 0; i < pack_elems.size(); i += 1) {
|
|
|
|
|
PPackage*pkg = pack_elems[i].pack;
|
|
|
|
|
NetScope*scope = pack_elems[i].scope;
|
|
|
|
|
rc &= pkg->elaborate(des, scope);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-16 02:33:56 +02:00
|
|
|
des->root_elaborate();
|
|
|
|
|
|
2013-04-07 02:38:36 +02:00
|
|
|
for (i = 0; i < root_elems.size(); i++) {
|
|
|
|
|
Module *rmod = root_elems[i].mod;
|
|
|
|
|
NetScope *scope = root_elems[i].scope;
|
2001-10-19 23:53:24 +02:00
|
|
|
rc &= rmod->elaborate(des, scope);
|
2000-05-02 18:27:38 +02:00
|
|
|
}
|
|
|
|
|
|
1999-01-25 06:45:56 +01:00
|
|
|
if (rc == false) {
|
2008-08-29 00:05:19 +02:00
|
|
|
delete des;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now that everything is fully elaborated verify that we do
|
2011-03-30 08:42:26 +02:00
|
|
|
// not have an always block with no delay (an infinite loop),
|
|
|
|
|
// or a final block with a delay.
|
|
|
|
|
if (des->check_proc_delay() == false) {
|
1999-01-25 06:45:56 +01:00
|
|
|
delete des;
|
|
|
|
|
des = 0;
|
|
|
|
|
}
|
2000-03-08 05:36:53 +01:00
|
|
|
|
2012-06-04 21:43:33 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << "<toplevel>" << ": debug: "
|
|
|
|
|
<< " finishing with "
|
|
|
|
|
<< des->find_root_scopes().size() << " root scopes " << endl;
|
|
|
|
|
}
|
|
|
|
|
|
1998-11-04 00:28:49 +01:00
|
|
|
return des;
|
|
|
|
|
}
|