2001-02-11 03:15:52 +01:00
|
|
|
/*
|
2021-02-17 08:45:27 +01:00
|
|
|
* Copyright (c) 2001-2021 Stephen Williams (steve@icarus.com)
|
2001-02-11 03:15:52 +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.
|
2001-02-11 03:15:52 +01:00
|
|
|
*/
|
|
|
|
|
|
2001-07-25 05:10:48 +02:00
|
|
|
# include "config.h"
|
|
|
|
|
|
2008-01-05 00:23:47 +01:00
|
|
|
# include <cstdlib>
|
2014-08-25 21:27:22 +02:00
|
|
|
# include <climits>
|
2001-02-11 03:15:52 +01:00
|
|
|
# include "netlist.h"
|
2014-09-02 18:23:54 +02:00
|
|
|
# include "netparray.h"
|
2012-09-15 19:27:43 +02:00
|
|
|
# include "netvector.h"
|
2001-02-11 03:15:52 +01:00
|
|
|
# include "netmisc.h"
|
|
|
|
|
# include "PExpr.h"
|
2007-06-02 05:42:12 +02:00
|
|
|
# include "pform_types.h"
|
2010-11-06 03:49:28 +01:00
|
|
|
# include "compiler.h"
|
2007-06-02 05:42:12 +02:00
|
|
|
# include "ivl_assert.h"
|
2001-02-11 03:15:52 +01:00
|
|
|
|
2021-11-04 17:12:04 +01:00
|
|
|
using namespace std;
|
2002-08-31 05:48:50 +02:00
|
|
|
|
2008-08-17 17:21:24 +02:00
|
|
|
NetNet* sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig)
|
|
|
|
|
{
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*zero_vec = new netvector_t(sig->data_type(),
|
|
|
|
|
sig->vector_width()-1, 0);
|
2008-08-17 17:21:24 +02:00
|
|
|
NetNet*zero_net = new NetNet(scope, scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::WIRE, zero_vec);
|
2011-02-10 06:03:08 +01:00
|
|
|
zero_net->set_line(*sig);
|
2008-08-17 17:21:24 +02:00
|
|
|
zero_net->local_flag(true);
|
|
|
|
|
|
2008-08-24 03:11:11 +02:00
|
|
|
if (sig->data_type() == IVL_VT_REAL) {
|
|
|
|
|
verireal zero (val);
|
|
|
|
|
NetLiteral*zero_obj = new NetLiteral(scope, scope->local_symbol(), zero);
|
2011-02-10 06:03:08 +01:00
|
|
|
zero_obj->set_line(*sig);
|
2008-08-24 03:11:11 +02:00
|
|
|
des->add_node(zero_obj);
|
|
|
|
|
|
|
|
|
|
connect(zero_net->pin(0), zero_obj->pin(0));
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
verinum zero ((int64_t)val);
|
2014-01-05 21:53:58 +01:00
|
|
|
zero = cast_to_width(zero, sig->vector_width());
|
2013-02-15 20:49:10 +01:00
|
|
|
zero.has_sign(sig->get_signed());
|
2008-08-24 03:11:11 +02:00
|
|
|
NetConst*zero_obj = new NetConst(scope, scope->local_symbol(), zero);
|
2011-02-10 06:03:08 +01:00
|
|
|
zero_obj->set_line(*sig);
|
2008-08-24 03:11:11 +02:00
|
|
|
des->add_node(zero_obj);
|
|
|
|
|
|
|
|
|
|
connect(zero_net->pin(0), zero_obj->pin(0));
|
|
|
|
|
}
|
2008-08-17 17:21:24 +02:00
|
|
|
|
|
|
|
|
NetAddSub*adder = new NetAddSub(scope, scope->local_symbol(), sig->vector_width());
|
2011-02-10 06:03:08 +01:00
|
|
|
adder->set_line(*sig);
|
2008-08-17 17:21:24 +02:00
|
|
|
des->add_node(adder);
|
|
|
|
|
adder->attribute(perm_string::literal("LPM_Direction"), verinum("SUB"));
|
|
|
|
|
|
|
|
|
|
connect(zero_net->pin(0), adder->pin_DataA());
|
|
|
|
|
connect(adder->pin_DataB(), sig->pin(0));
|
|
|
|
|
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*tmp_vec = new netvector_t(sig->data_type(),
|
|
|
|
|
sig->vector_width()-1, 0);
|
2008-08-17 17:21:24 +02:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::WIRE, tmp_vec);
|
2011-02-10 06:03:08 +01:00
|
|
|
tmp->set_line(*sig);
|
2008-08-17 17:21:24 +02:00
|
|
|
tmp->local_flag(true);
|
|
|
|
|
|
|
|
|
|
connect(adder->pin_Result(), tmp->pin(0));
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-16 19:53:20 +02:00
|
|
|
NetNet* cast_to_int2(Design*des, NetScope*scope, NetNet*src, unsigned wid)
|
|
|
|
|
{
|
|
|
|
|
if (src->data_type() == IVL_VT_BOOL)
|
|
|
|
|
return src;
|
|
|
|
|
|
2013-02-25 23:13:05 +01:00
|
|
|
netvector_t*tmp_vec = new netvector_t(IVL_VT_BOOL, wid-1, 0,
|
|
|
|
|
src->get_signed());
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec);
|
2010-10-16 19:53:20 +02:00
|
|
|
tmp->set_line(*src);
|
|
|
|
|
tmp->local_flag(true);
|
|
|
|
|
|
|
|
|
|
NetCastInt2*cast = new NetCastInt2(scope, scope->local_symbol(), wid);
|
|
|
|
|
cast->set_line(*src);
|
|
|
|
|
des->add_node(cast);
|
|
|
|
|
|
|
|
|
|
connect(cast->pin(0), tmp->pin(0));
|
|
|
|
|
connect(cast->pin(1), src->pin(0));
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNet* cast_to_int4(Design*des, NetScope*scope, NetNet*src, unsigned wid)
|
2008-06-21 03:11:11 +02:00
|
|
|
{
|
|
|
|
|
if (src->data_type() != IVL_VT_REAL)
|
|
|
|
|
return src;
|
|
|
|
|
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*tmp_vec = new netvector_t(IVL_VT_LOGIC, wid-1, 0);
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec);
|
2008-06-21 03:11:11 +02:00
|
|
|
tmp->set_line(*src);
|
|
|
|
|
tmp->local_flag(true);
|
|
|
|
|
|
2010-10-16 19:53:20 +02:00
|
|
|
NetCastInt4*cast = new NetCastInt4(scope, scope->local_symbol(), wid);
|
2008-06-21 03:11:11 +02:00
|
|
|
cast->set_line(*src);
|
|
|
|
|
des->add_node(cast);
|
|
|
|
|
|
|
|
|
|
connect(cast->pin(0), tmp->pin(0));
|
|
|
|
|
connect(cast->pin(1), src->pin(0));
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-18 02:07:19 +02:00
|
|
|
NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src)
|
|
|
|
|
{
|
|
|
|
|
if (src->data_type() == IVL_VT_REAL)
|
|
|
|
|
return src;
|
|
|
|
|
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*tmp_vec = new netvector_t(IVL_VT_REAL);
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec);
|
2008-06-18 02:07:19 +02:00
|
|
|
tmp->set_line(*src);
|
2008-06-21 03:11:11 +02:00
|
|
|
tmp->local_flag(true);
|
2008-06-18 02:07:19 +02:00
|
|
|
|
|
|
|
|
NetCastReal*cast = new NetCastReal(scope, scope->local_symbol(), src->get_signed());
|
|
|
|
|
cast->set_line(*src);
|
|
|
|
|
des->add_node(cast);
|
|
|
|
|
|
|
|
|
|
connect(cast->pin(0), tmp->pin(0));
|
|
|
|
|
connect(cast->pin(1), src->pin(0));
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-25 21:32:56 +01:00
|
|
|
NetExpr* cast_to_int2(NetExpr*expr, unsigned width)
|
|
|
|
|
{
|
|
|
|
|
// Special case: The expression is already BOOL
|
|
|
|
|
if (expr->expr_type() == IVL_VT_BOOL)
|
|
|
|
|
return expr;
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << expr->get_fileline() << ": debug: "
|
|
|
|
|
<< "Cast expression to int2, width=" << width << "." << endl;
|
|
|
|
|
|
|
|
|
|
NetECast*cast = new NetECast('2', expr, width, expr->has_sign());
|
|
|
|
|
cast->set_line(*expr);
|
|
|
|
|
return cast;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr* cast_to_int4(NetExpr*expr, unsigned width)
|
|
|
|
|
{
|
|
|
|
|
// Special case: The expression is already LOGIC or BOOL
|
2019-09-11 23:08:46 +02:00
|
|
|
if (expr->expr_type() == IVL_VT_LOGIC || expr->expr_type() == IVL_VT_BOOL)
|
2013-02-25 21:32:56 +01:00
|
|
|
return expr;
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << expr->get_fileline() << ": debug: "
|
|
|
|
|
<< "Cast expression to int4, width=" << width << "." << endl;
|
|
|
|
|
|
|
|
|
|
NetECast*cast = new NetECast('v', expr, width, expr->has_sign());
|
|
|
|
|
cast->set_line(*expr);
|
|
|
|
|
return cast;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-08 05:29:24 +02:00
|
|
|
NetExpr* cast_to_real(NetExpr*expr)
|
|
|
|
|
{
|
|
|
|
|
if (expr->expr_type() == IVL_VT_REAL)
|
|
|
|
|
return expr;
|
|
|
|
|
|
2013-02-25 21:32:56 +01:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << expr->get_fileline() << ": debug: "
|
|
|
|
|
<< "Cast expression to real." << endl;
|
|
|
|
|
|
2012-10-08 05:29:24 +02:00
|
|
|
NetECast*cast = new NetECast('r', expr, 1, true);
|
|
|
|
|
cast->set_line(*expr);
|
|
|
|
|
return cast;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-24 06:28:30 +01:00
|
|
|
/*
|
|
|
|
|
* Add a signed constant to an existing expression. Generate a new
|
|
|
|
|
* NetEBAdd node that has the input expression and an expression made
|
|
|
|
|
* from the constant value.
|
|
|
|
|
*/
|
2010-10-02 20:02:27 +02:00
|
|
|
static NetExpr* make_add_expr(NetExpr*expr, long val)
|
2005-01-24 06:28:30 +01:00
|
|
|
{
|
|
|
|
|
if (val == 0)
|
|
|
|
|
return expr;
|
|
|
|
|
|
|
|
|
|
// If the value to be added is <0, then instead generate a
|
|
|
|
|
// SUBTRACT node and turn the value positive.
|
|
|
|
|
char add_op = '+';
|
|
|
|
|
if (val < 0) {
|
|
|
|
|
add_op = '-';
|
|
|
|
|
val = -val;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
verinum val_v (val, expr->expr_width());
|
2016-07-10 14:30:58 +02:00
|
|
|
val_v.has_sign(expr->has_sign());
|
2005-04-08 06:50:31 +02:00
|
|
|
|
2005-01-24 06:28:30 +01:00
|
|
|
NetEConst*val_c = new NetEConst(val_v);
|
|
|
|
|
val_c->set_line(*expr);
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
NetEBAdd*res = new NetEBAdd(add_op, expr, val_c, expr->expr_width(),
|
|
|
|
|
expr->has_sign());
|
2005-01-24 06:28:30 +01:00
|
|
|
res->set_line(*expr);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-20 02:27:48 +02:00
|
|
|
static NetExpr* make_add_expr(const LineInfo*loc, NetExpr*expr1, NetExpr*expr2)
|
|
|
|
|
{
|
|
|
|
|
bool use_signed = expr1->has_sign() && expr2->has_sign();
|
|
|
|
|
unsigned use_wid = expr1->expr_width();
|
|
|
|
|
|
|
|
|
|
if (expr2->expr_width() > use_wid)
|
|
|
|
|
use_wid = expr2->expr_width();
|
|
|
|
|
|
|
|
|
|
expr1 = pad_to_width(expr1, use_wid, *loc);
|
|
|
|
|
expr2 = pad_to_width(expr2, use_wid, *loc);
|
|
|
|
|
|
|
|
|
|
NetEBAdd*tmp = new NetEBAdd('+', expr1, expr2, use_wid, use_signed);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-02 20:02:27 +02:00
|
|
|
/*
|
|
|
|
|
* Subtract an existing expression from a signed constant.
|
|
|
|
|
*/
|
|
|
|
|
static NetExpr* make_sub_expr(long val, NetExpr*expr)
|
2005-01-24 06:28:30 +01:00
|
|
|
{
|
|
|
|
|
verinum val_v (val, expr->expr_width());
|
2016-07-10 14:30:58 +02:00
|
|
|
val_v.has_sign(expr->has_sign());
|
2010-10-02 20:02:27 +02:00
|
|
|
|
2005-01-24 06:28:30 +01:00
|
|
|
NetEConst*val_c = new NetEConst(val_v);
|
|
|
|
|
val_c->set_line(*expr);
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
NetEBAdd*res = new NetEBAdd('-', val_c, expr, expr->expr_width(),
|
|
|
|
|
expr->has_sign());
|
2005-01-24 06:28:30 +01:00
|
|
|
res->set_line(*expr);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2001-02-11 03:15:52 +01:00
|
|
|
|
2016-03-01 16:17:07 +01:00
|
|
|
/*
|
|
|
|
|
* Subtract a signed constant from an existing expression.
|
|
|
|
|
*/
|
|
|
|
|
static NetExpr* make_sub_expr(NetExpr*expr, long val)
|
|
|
|
|
{
|
|
|
|
|
verinum val_v (val, expr->expr_width());
|
2016-07-10 14:30:58 +02:00
|
|
|
val_v.has_sign(expr->has_sign());
|
2016-03-01 16:17:07 +01:00
|
|
|
|
|
|
|
|
NetEConst*val_c = new NetEConst(val_v);
|
|
|
|
|
val_c->set_line(*expr);
|
|
|
|
|
|
|
|
|
|
NetEBAdd*res = new NetEBAdd('-', expr, val_c, expr->expr_width(),
|
|
|
|
|
expr->has_sign());
|
|
|
|
|
res->set_line(*expr);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-01-19 01:04:25 +01:00
|
|
|
/*
|
2016-07-10 14:30:58 +02:00
|
|
|
* Multiply an existing expression by a signed positive number.
|
2014-01-19 01:04:25 +01:00
|
|
|
* This does a lossless multiply, so the arguments will need to be
|
|
|
|
|
* sized to match the output size.
|
|
|
|
|
*/
|
2012-02-11 02:17:59 +01:00
|
|
|
static NetExpr* make_mult_expr(NetExpr*expr, unsigned long val)
|
|
|
|
|
{
|
2014-01-19 01:04:25 +01:00
|
|
|
const unsigned val_wid = ceil(log2((double)val)) ;
|
|
|
|
|
unsigned use_wid = expr->expr_width() + val_wid;
|
|
|
|
|
verinum val_v (val, use_wid);
|
2016-07-10 14:30:58 +02:00
|
|
|
val_v.has_sign(expr->has_sign());
|
2012-02-11 02:17:59 +01:00
|
|
|
|
|
|
|
|
NetEConst*val_c = new NetEConst(val_v);
|
|
|
|
|
val_c->set_line(*expr);
|
|
|
|
|
|
2014-01-19 01:04:25 +01:00
|
|
|
// We know by definitions that the expr argument needs to be
|
|
|
|
|
// padded to be the right argument width for this lossless multiply.
|
|
|
|
|
expr = pad_to_width(expr, use_wid, *expr);
|
|
|
|
|
|
|
|
|
|
NetEBMult*res = new NetEBMult('*', expr, val_c, use_wid, expr->has_sign());
|
2012-02-11 02:17:59 +01:00
|
|
|
res->set_line(*expr);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-02 20:02:27 +02:00
|
|
|
/*
|
|
|
|
|
* This routine is used to calculate the number of bits needed to
|
|
|
|
|
* contain the given number.
|
|
|
|
|
*/
|
|
|
|
|
static unsigned num_bits(long arg)
|
|
|
|
|
{
|
|
|
|
|
unsigned res = 0;
|
|
|
|
|
|
|
|
|
|
/* For a negative value we have room for one extra value, but
|
|
|
|
|
* we have a signed result so we need an extra bit for this. */
|
|
|
|
|
if (arg < 0) {
|
|
|
|
|
arg = -arg - 1;
|
|
|
|
|
res += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Calculate the number of bits needed here. */
|
|
|
|
|
while (arg) {
|
|
|
|
|
res += 1;
|
|
|
|
|
arg >>= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This routine generates the normalization expression needed for a variable
|
2012-02-12 19:13:20 +01:00
|
|
|
* bit select or a variable base expression for an indexed part
|
|
|
|
|
* select. This function doesn't actually look at the variable
|
|
|
|
|
* dimensions, it just does the final calculation using msb/lsb of the
|
|
|
|
|
* last slice, and the off of the slice in the variable.
|
2010-10-02 20:02:27 +02:00
|
|
|
*/
|
|
|
|
|
NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb,
|
2012-02-12 19:13:20 +01:00
|
|
|
unsigned long wid, bool is_up, long soff)
|
2010-10-02 20:02:27 +02:00
|
|
|
{
|
|
|
|
|
long offset = lsb;
|
|
|
|
|
|
|
|
|
|
if (msb < lsb) {
|
|
|
|
|
/* Correct the offset if needed. */
|
|
|
|
|
if (is_up) offset -= wid - 1;
|
|
|
|
|
/* Calculate the space needed for the offset. */
|
|
|
|
|
unsigned min_wid = num_bits(offset);
|
2012-08-20 02:27:48 +02:00
|
|
|
if (num_bits(soff) > min_wid)
|
|
|
|
|
min_wid = num_bits(soff);
|
2010-10-02 20:02:27 +02:00
|
|
|
/* We need enough space for the larger of the offset or the
|
|
|
|
|
* base expression. */
|
|
|
|
|
if (min_wid < base->expr_width()) min_wid = base->expr_width();
|
|
|
|
|
/* Now that we have the minimum needed width increase it by
|
|
|
|
|
* one to make room for the normalization calculation. */
|
2014-03-29 19:23:16 +01:00
|
|
|
min_wid += 2;
|
2010-10-02 20:02:27 +02:00
|
|
|
/* Pad the base expression to the correct width. */
|
|
|
|
|
base = pad_to_width(base, min_wid, *base);
|
|
|
|
|
/* If the base expression is unsigned and either the lsb
|
|
|
|
|
* is negative or it does not fill the width of the base
|
|
|
|
|
* expression then we could generate negative normalized
|
|
|
|
|
* values so cast the expression to signed to get the
|
|
|
|
|
* math correct. */
|
|
|
|
|
if ((lsb < 0 || num_bits(lsb+1) <= base->expr_width()) &&
|
|
|
|
|
! base->has_sign()) {
|
|
|
|
|
/* We need this extra select to hide the signed
|
|
|
|
|
* property from the padding above. It will be
|
|
|
|
|
* removed automatically during code generation. */
|
|
|
|
|
NetESelect *tmp = new NetESelect(base, 0 , min_wid);
|
|
|
|
|
tmp->set_line(*base);
|
|
|
|
|
tmp->cast_signed(true);
|
|
|
|
|
base = tmp;
|
|
|
|
|
}
|
|
|
|
|
/* Normalize the expression. */
|
2012-02-12 19:13:20 +01:00
|
|
|
base = make_sub_expr(offset+soff, base);
|
2010-10-02 20:02:27 +02:00
|
|
|
} else {
|
|
|
|
|
/* Correct the offset if needed. */
|
|
|
|
|
if (!is_up) offset += wid - 1;
|
|
|
|
|
/* If the offset is zero then just return the base (index)
|
|
|
|
|
* expression. */
|
2012-02-12 19:13:20 +01:00
|
|
|
if ((soff-offset) == 0) return base;
|
2010-10-02 20:02:27 +02:00
|
|
|
/* Calculate the space needed for the offset. */
|
|
|
|
|
unsigned min_wid = num_bits(-offset);
|
2012-08-20 02:27:48 +02:00
|
|
|
if (num_bits(soff) > min_wid)
|
|
|
|
|
min_wid = num_bits(soff);
|
2010-10-02 20:02:27 +02:00
|
|
|
/* We need enough space for the larger of the offset or the
|
|
|
|
|
* base expression. */
|
|
|
|
|
if (min_wid < base->expr_width()) min_wid = base->expr_width();
|
|
|
|
|
/* Now that we have the minimum needed width increase it by
|
|
|
|
|
* one to make room for the normalization calculation. */
|
2014-03-29 19:23:16 +01:00
|
|
|
min_wid += 2;
|
2010-10-02 20:02:27 +02:00
|
|
|
/* Pad the base expression to the correct width. */
|
|
|
|
|
base = pad_to_width(base, min_wid, *base);
|
|
|
|
|
/* If the offset is greater than zero then we need to do
|
|
|
|
|
* signed math to get the location value correct. */
|
|
|
|
|
if (offset > 0 && ! base->has_sign()) {
|
|
|
|
|
/* We need this extra select to hide the signed
|
|
|
|
|
* property from the padding above. It will be
|
|
|
|
|
* removed automatically during code generation. */
|
|
|
|
|
NetESelect *tmp = new NetESelect(base, 0 , min_wid);
|
|
|
|
|
tmp->set_line(*base);
|
|
|
|
|
tmp->cast_signed(true);
|
|
|
|
|
base = tmp;
|
|
|
|
|
}
|
|
|
|
|
/* Normalize the expression. */
|
2012-02-12 19:13:20 +01:00
|
|
|
base = make_add_expr(base, soff-offset);
|
2010-10-02 20:02:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return base;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 02:47:53 +01:00
|
|
|
/*
|
|
|
|
|
* This method is how indices should work except that the base should
|
|
|
|
|
* be a vector of expressions that matches the size of the dims list,
|
|
|
|
|
* so that we can generate an expression based on the entire packed
|
|
|
|
|
* vector. For now, we assert that there is only one set of dimensions.
|
|
|
|
|
*/
|
|
|
|
|
NetExpr *normalize_variable_base(NetExpr *base,
|
2012-03-26 02:59:05 +02:00
|
|
|
const list<netrange_t>&dims,
|
2012-02-07 02:47:53 +01:00
|
|
|
unsigned long wid, bool is_up)
|
|
|
|
|
{
|
|
|
|
|
ivl_assert(*base, dims.size() == 1);
|
2012-03-26 02:59:05 +02:00
|
|
|
const netrange_t&rng = dims.back();
|
2012-07-14 03:41:41 +02:00
|
|
|
return normalize_variable_base(base, rng.get_msb(), rng.get_lsb(), wid, is_up);
|
2012-02-07 02:47:53 +01:00
|
|
|
}
|
|
|
|
|
|
2012-02-12 19:13:20 +01:00
|
|
|
NetExpr *normalize_variable_bit_base(const list<long>&indices, NetExpr*base,
|
|
|
|
|
const NetNet*reg)
|
|
|
|
|
{
|
2012-09-30 00:13:45 +02:00
|
|
|
const vector<netrange_t>&packed_dims = reg->packed_dims();
|
2012-02-12 19:13:20 +01:00
|
|
|
ivl_assert(*base, indices.size()+1 == packed_dims.size());
|
|
|
|
|
|
|
|
|
|
// Get the canonical offset of the slice within which we are
|
|
|
|
|
// addressing. We need that address as a slice offset to
|
|
|
|
|
// calculate the proper complete address
|
2012-03-26 02:59:05 +02:00
|
|
|
const netrange_t&rng = packed_dims.back();
|
2012-07-14 03:41:41 +02:00
|
|
|
long slice_off = reg->sb_to_idx(indices, rng.get_lsb());
|
2012-02-12 19:13:20 +01:00
|
|
|
|
2012-07-14 03:41:41 +02:00
|
|
|
return normalize_variable_base(base, rng.get_msb(), rng.get_lsb(), 1, true, slice_off);
|
2012-02-12 19:13:20 +01:00
|
|
|
}
|
|
|
|
|
|
2012-02-12 23:52:47 +01:00
|
|
|
NetExpr *normalize_variable_part_base(const list<long>&indices, NetExpr*base,
|
|
|
|
|
const NetNet*reg,
|
|
|
|
|
unsigned long wid, bool is_up)
|
|
|
|
|
{
|
2012-09-30 00:13:45 +02:00
|
|
|
const vector<netrange_t>&packed_dims = reg->packed_dims();
|
2012-02-12 23:52:47 +01:00
|
|
|
ivl_assert(*base, indices.size()+1 == packed_dims.size());
|
|
|
|
|
|
|
|
|
|
// Get the canonical offset of the slice within which we are
|
|
|
|
|
// addressing. We need that address as a slice offset to
|
|
|
|
|
// calculate the proper complete address
|
2012-03-26 02:59:05 +02:00
|
|
|
const netrange_t&rng = packed_dims.back();
|
2012-07-14 03:41:41 +02:00
|
|
|
long slice_off = reg->sb_to_idx(indices, rng.get_lsb());
|
2012-02-12 23:52:47 +01:00
|
|
|
|
2012-07-14 03:41:41 +02:00
|
|
|
return normalize_variable_base(base, rng.get_msb(), rng.get_lsb(), wid, is_up, slice_off);
|
2012-02-12 23:52:47 +01:00
|
|
|
}
|
|
|
|
|
|
2012-02-11 02:17:59 +01:00
|
|
|
NetExpr *normalize_variable_slice_base(const list<long>&indices, NetExpr*base,
|
|
|
|
|
const NetNet*reg, unsigned long&lwid)
|
|
|
|
|
{
|
2012-09-30 00:13:45 +02:00
|
|
|
const vector<netrange_t>&packed_dims = reg->packed_dims();
|
2012-02-11 02:17:59 +01:00
|
|
|
ivl_assert(*base, indices.size() < packed_dims.size());
|
|
|
|
|
|
2012-09-30 00:13:45 +02:00
|
|
|
vector<netrange_t>::const_iterator pcur = packed_dims.end();
|
2012-02-11 02:17:59 +01:00
|
|
|
for (size_t idx = indices.size() ; idx < packed_dims.size(); idx += 1) {
|
|
|
|
|
-- pcur;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 16:17:07 +01:00
|
|
|
long sb = min(pcur->get_lsb(), pcur->get_msb());
|
2012-02-11 02:17:59 +01:00
|
|
|
long loff;
|
|
|
|
|
reg->sb_to_slice(indices, sb, loff, lwid);
|
|
|
|
|
|
2016-07-10 14:30:58 +02:00
|
|
|
unsigned min_wid = base->expr_width();
|
|
|
|
|
if ((sb < 0) && !base->has_sign()) min_wid += 1;
|
|
|
|
|
if (min_wid < num_bits(pcur->get_lsb())) min_wid = pcur->get_lsb();
|
|
|
|
|
if (min_wid < num_bits(pcur->get_msb())) min_wid = pcur->get_msb();
|
2016-07-10 00:33:33 +02:00
|
|
|
base = pad_to_width(base, min_wid, *base);
|
2016-07-10 14:30:58 +02:00
|
|
|
if ((sb < 0) && !base->has_sign()) {
|
|
|
|
|
NetESelect *tmp = new NetESelect(base, 0 , min_wid);
|
|
|
|
|
tmp->set_line(*base);
|
|
|
|
|
tmp->cast_signed(true);
|
|
|
|
|
base = tmp;
|
|
|
|
|
}
|
2016-07-10 00:33:33 +02:00
|
|
|
|
|
|
|
|
if (pcur->get_msb() >= pcur->get_lsb()) {
|
|
|
|
|
if (pcur->get_lsb() != 0)
|
|
|
|
|
base = make_sub_expr(base, pcur->get_lsb());
|
|
|
|
|
base = make_mult_expr(base, lwid);
|
2016-07-10 14:30:58 +02:00
|
|
|
min_wid = base->expr_width();
|
|
|
|
|
if (min_wid < num_bits(loff)) min_wid = num_bits(loff);
|
|
|
|
|
if (loff != 0) min_wid += 1;
|
|
|
|
|
base = pad_to_width(base, min_wid, *base);
|
2016-07-10 00:33:33 +02:00
|
|
|
base = make_add_expr(base, loff);
|
|
|
|
|
} else {
|
|
|
|
|
if (pcur->get_msb() != 0)
|
|
|
|
|
base = make_sub_expr(base, pcur->get_msb());
|
|
|
|
|
base = make_mult_expr(base, lwid);
|
2016-07-10 14:30:58 +02:00
|
|
|
min_wid = base->expr_width();
|
|
|
|
|
if (min_wid < num_bits(loff)) min_wid = num_bits(loff);
|
|
|
|
|
if (loff != 0) min_wid += 1;
|
|
|
|
|
base = pad_to_width(base, min_wid, *base);
|
2016-07-10 00:33:33 +02:00
|
|
|
base = make_sub_expr(loff, base);
|
2016-03-01 16:17:07 +01:00
|
|
|
}
|
2012-02-11 02:17:59 +01:00
|
|
|
return base;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-26 00:58:29 +02:00
|
|
|
ostream& operator << (ostream&o, __IndicesManip<long> val)
|
|
|
|
|
{
|
|
|
|
|
for (list<long>::const_iterator cur = val.val.begin()
|
|
|
|
|
; cur != val.val.end() ; ++cur) {
|
|
|
|
|
o << "[" << *cur << "]";
|
|
|
|
|
}
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ostream& operator << (ostream&o, __IndicesManip<NetExpr*> val)
|
|
|
|
|
{
|
|
|
|
|
for (list<NetExpr*>::const_iterator cur = val.val.begin()
|
|
|
|
|
; cur != val.val.end() ; ++cur) {
|
|
|
|
|
o << "[" << *(*cur) << "]";
|
|
|
|
|
}
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-02 20:02:27 +02:00
|
|
|
/*
|
2012-05-26 00:58:29 +02:00
|
|
|
* The src is the input index expression list from the expression, and
|
|
|
|
|
* the count is the number that are to be elaborated into the indices
|
|
|
|
|
* list. At the same time, create a indices_const list that contains
|
2013-05-18 20:21:37 +02:00
|
|
|
* the evaluated values for the expression, if they can be evaluated.
|
2010-10-02 20:02:27 +02:00
|
|
|
*/
|
2013-05-18 20:21:37 +02:00
|
|
|
void indices_to_expressions(Design*des, NetScope*scope,
|
2012-05-26 00:58:29 +02:00
|
|
|
// loc is for error messages.
|
|
|
|
|
const LineInfo*loc,
|
|
|
|
|
// src is the index list, and count is
|
|
|
|
|
// the number of items in the list to use.
|
|
|
|
|
const list<index_component_t>&src, unsigned count,
|
|
|
|
|
// True if the expression MUST be constant.
|
|
|
|
|
bool need_const,
|
|
|
|
|
// These are the outputs.
|
2013-05-18 20:21:37 +02:00
|
|
|
indices_flags&flags,
|
2012-05-26 00:58:29 +02:00
|
|
|
list<NetExpr*>&indices, list<long>&indices_const)
|
|
|
|
|
{
|
|
|
|
|
ivl_assert(*loc, count <= src.size());
|
|
|
|
|
|
2013-05-18 20:21:37 +02:00
|
|
|
flags.invalid = false;
|
|
|
|
|
flags.variable = false;
|
|
|
|
|
flags.undefined = false;
|
2012-05-26 00:58:29 +02:00
|
|
|
for (list<index_component_t>::const_iterator cur = src.begin()
|
|
|
|
|
; count > 0 ; ++cur, --count) {
|
|
|
|
|
ivl_assert(*loc, cur->sel != index_component_t::SEL_NONE);
|
|
|
|
|
|
|
|
|
|
if (cur->sel != index_component_t::SEL_BIT) {
|
|
|
|
|
cerr << loc->get_fileline() << ": error: "
|
|
|
|
|
<< "Array cannot be indexed by a range." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
ivl_assert(*loc, cur->msb);
|
|
|
|
|
|
2021-05-16 18:19:13 +02:00
|
|
|
NetExpr*word_index = elab_and_eval(des, scope, cur->msb, -1, need_const);
|
2012-05-26 00:58:29 +02:00
|
|
|
|
|
|
|
|
if (word_index == 0)
|
2013-05-18 20:21:37 +02:00
|
|
|
flags.invalid = true;
|
2012-05-26 00:58:29 +02:00
|
|
|
|
|
|
|
|
// Track if we detect any non-constant expressions
|
|
|
|
|
// here. This may allow for a special case.
|
2013-05-18 20:21:37 +02:00
|
|
|
NetEConst*word_const = dynamic_cast<NetEConst*> (word_index);
|
|
|
|
|
if (word_const == 0)
|
|
|
|
|
flags.variable = true;
|
|
|
|
|
else if (!word_const->value().is_defined())
|
|
|
|
|
flags.undefined = true;
|
|
|
|
|
else if (!flags.variable && !flags.undefined)
|
|
|
|
|
indices_const.push_back(word_const->value().as_long());
|
2012-05-26 00:58:29 +02:00
|
|
|
|
|
|
|
|
indices.push_back(word_index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-26 01:32:12 +02:00
|
|
|
static void make_strides(const vector<netrange_t>&dims,
|
2012-05-26 00:58:29 +02:00
|
|
|
vector<long>&stride)
|
2010-10-02 20:02:27 +02:00
|
|
|
{
|
2012-05-26 00:58:29 +02:00
|
|
|
stride[dims.size()-1] = 1;
|
|
|
|
|
for (size_t idx = stride.size()-1 ; idx > 0 ; --idx) {
|
|
|
|
|
long tmp = dims[idx].width();
|
|
|
|
|
if (idx < stride.size())
|
|
|
|
|
tmp *= stride[idx];
|
|
|
|
|
stride[idx-1] = tmp;
|
2010-10-02 20:02:27 +02:00
|
|
|
}
|
2012-05-26 00:58:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Take in a vector of constant indices and convert them to a single
|
|
|
|
|
* number that is the canonical address (zero based, 1-d) of the
|
|
|
|
|
* word. If any of the indices are out of bounds, return nil instead
|
|
|
|
|
* of an expression.
|
|
|
|
|
*/
|
2014-09-02 18:23:54 +02:00
|
|
|
static NetExpr* normalize_variable_unpacked(const vector<netrange_t>&dims, list<long>&indices)
|
2012-05-26 00:58:29 +02:00
|
|
|
{
|
|
|
|
|
// Make strides for each index. The stride is the distance (in
|
|
|
|
|
// words) to the next element in the canonical array.
|
|
|
|
|
vector<long> stride (dims.size());
|
|
|
|
|
make_strides(dims, stride);
|
|
|
|
|
|
|
|
|
|
int64_t canonical_addr = 0;
|
|
|
|
|
|
|
|
|
|
int idx = 0;
|
|
|
|
|
for (list<long>::const_iterator cur = indices.begin()
|
|
|
|
|
; cur != indices.end() ; ++cur, ++idx) {
|
|
|
|
|
long tmp = *cur;
|
|
|
|
|
|
2012-07-14 03:41:41 +02:00
|
|
|
if (dims[idx].get_lsb() <= dims[idx].get_msb())
|
|
|
|
|
tmp -= dims[idx].get_lsb();
|
2012-05-26 00:58:29 +02:00
|
|
|
else
|
2012-07-14 03:41:41 +02:00
|
|
|
tmp -= dims[idx].get_msb();
|
2012-05-26 00:58:29 +02:00
|
|
|
|
|
|
|
|
// Notice of this index is out of range.
|
2012-05-26 01:32:12 +02:00
|
|
|
if (tmp < 0 || tmp >= (long)dims[idx].width()) {
|
2012-05-26 00:58:29 +02:00
|
|
|
return 0;
|
2010-10-02 20:02:27 +02:00
|
|
|
}
|
2012-05-26 00:58:29 +02:00
|
|
|
|
|
|
|
|
canonical_addr += tmp * stride[idx];
|
2010-10-02 20:02:27 +02:00
|
|
|
}
|
|
|
|
|
|
2012-05-26 00:58:29 +02:00
|
|
|
NetEConst*canonical_expr = new NetEConst(verinum(canonical_addr));
|
|
|
|
|
return canonical_expr;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-02 18:23:54 +02:00
|
|
|
NetExpr* normalize_variable_unpacked(const NetNet*net, list<long>&indices)
|
2012-05-26 00:58:29 +02:00
|
|
|
{
|
2012-05-26 01:32:12 +02:00
|
|
|
const vector<netrange_t>&dims = net->unpacked_dims();
|
2014-09-02 18:23:54 +02:00
|
|
|
return normalize_variable_unpacked(dims, indices);
|
|
|
|
|
}
|
2012-05-26 00:58:29 +02:00
|
|
|
|
2014-09-02 18:23:54 +02:00
|
|
|
NetExpr* normalize_variable_unpacked(const netsarray_t*stype, list<long>&indices)
|
|
|
|
|
{
|
|
|
|
|
const vector<netrange_t>&dims = stype->static_dimensions();
|
|
|
|
|
return normalize_variable_unpacked(dims, indices);
|
|
|
|
|
}
|
2012-05-26 00:58:29 +02:00
|
|
|
|
2014-09-02 18:23:54 +02:00
|
|
|
NetExpr* normalize_variable_unpacked(const LineInfo&loc, const vector<netrange_t>&dims, list<NetExpr*>&indices)
|
|
|
|
|
{
|
2012-05-26 00:58:29 +02:00
|
|
|
// Make strides for each index. The stride is the distance (in
|
|
|
|
|
// words) to the next element in the canonical array.
|
|
|
|
|
vector<long> stride (dims.size());
|
|
|
|
|
make_strides(dims, stride);
|
|
|
|
|
|
|
|
|
|
NetExpr*canonical_expr = 0;
|
|
|
|
|
|
|
|
|
|
int idx = 0;
|
|
|
|
|
for (list<NetExpr*>::const_iterator cur = indices.begin()
|
|
|
|
|
; cur != indices.end() ; ++cur, ++idx) {
|
|
|
|
|
NetExpr*tmp = *cur;
|
|
|
|
|
// If the expression elaboration generated errors, then
|
|
|
|
|
// give up. Presumably, the error during expression
|
|
|
|
|
// elaboration already generated the error message.
|
|
|
|
|
if (tmp == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
int64_t use_base;
|
2012-07-14 03:41:41 +02:00
|
|
|
if (! dims[idx].defined())
|
|
|
|
|
use_base = 0;
|
|
|
|
|
else if (dims[idx].get_lsb() <= dims[idx].get_msb())
|
|
|
|
|
use_base = dims[idx].get_lsb();
|
2012-05-26 00:58:29 +02:00
|
|
|
else
|
2012-07-14 03:41:41 +02:00
|
|
|
use_base = dims[idx].get_msb();
|
2012-05-26 00:58:29 +02:00
|
|
|
|
|
|
|
|
int64_t use_stride = stride[idx];
|
|
|
|
|
|
2012-11-01 20:34:38 +01:00
|
|
|
// Account for that we are doing arithmetic and should
|
2012-07-14 03:41:41 +02:00
|
|
|
// have a proper width to make sure there are no
|
2012-05-26 00:58:29 +02:00
|
|
|
// losses. So calculate a min_wid width.
|
|
|
|
|
unsigned tmp_wid;
|
|
|
|
|
unsigned min_wid = tmp->expr_width();
|
|
|
|
|
if (use_base != 0 && ((tmp_wid = num_bits(use_base)) >= min_wid))
|
|
|
|
|
min_wid = tmp_wid + 1;
|
|
|
|
|
if ((tmp_wid = num_bits(dims[idx].width()+1)) >= min_wid)
|
|
|
|
|
min_wid = tmp_wid + 1;
|
2014-08-11 21:23:29 +02:00
|
|
|
if (use_stride != 1)
|
|
|
|
|
min_wid += num_bits(use_stride);
|
2012-05-26 00:58:29 +02:00
|
|
|
|
2014-09-02 18:23:54 +02:00
|
|
|
tmp = pad_to_width(tmp, min_wid, loc);
|
2012-05-26 00:58:29 +02:00
|
|
|
|
|
|
|
|
// Now generate the math to calculate the canonical address.
|
|
|
|
|
NetExpr*tmp_scaled = 0;
|
|
|
|
|
if (NetEConst*tmp_const = dynamic_cast<NetEConst*> (tmp)) {
|
|
|
|
|
// Special case: the index is constant, so this
|
|
|
|
|
// iteration can be replaced with a constant
|
|
|
|
|
// expression.
|
|
|
|
|
int64_t val = tmp_const->value().as_long();
|
|
|
|
|
val -= use_base;
|
|
|
|
|
val *= use_stride;
|
2012-11-01 20:34:38 +01:00
|
|
|
// Very special case: the index is zero, so we can
|
|
|
|
|
// skip this iteration
|
|
|
|
|
if (val == 0)
|
|
|
|
|
continue;
|
2012-05-26 00:58:29 +02:00
|
|
|
tmp_scaled = new NetEConst(verinum(val));
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
tmp_scaled = tmp;
|
|
|
|
|
if (use_base != 0)
|
|
|
|
|
tmp_scaled = make_add_expr(tmp_scaled, -use_base);
|
|
|
|
|
if (use_stride != 1)
|
|
|
|
|
tmp_scaled = make_mult_expr(tmp_scaled, use_stride);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (canonical_expr == 0) {
|
|
|
|
|
canonical_expr = tmp_scaled;
|
|
|
|
|
} else {
|
2013-06-27 06:26:45 +02:00
|
|
|
bool expr_has_sign = canonical_expr->has_sign() &&
|
|
|
|
|
tmp_scaled->has_sign();
|
2012-05-26 00:58:29 +02:00
|
|
|
canonical_expr = new NetEBAdd('+', canonical_expr, tmp_scaled,
|
2013-06-27 06:26:45 +02:00
|
|
|
canonical_expr->expr_width()+1,
|
|
|
|
|
expr_has_sign);
|
2012-05-26 00:58:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-01 20:34:38 +01:00
|
|
|
// If we don't have an expression at this point, all the indices were
|
|
|
|
|
// constant zero. But this variant of normalize_variable_unpacked()
|
|
|
|
|
// is only used when at least one index is not a constant.
|
2014-09-02 18:23:54 +02:00
|
|
|
ivl_assert(loc, canonical_expr);
|
2012-11-01 20:34:38 +01:00
|
|
|
|
2012-05-26 00:58:29 +02:00
|
|
|
return canonical_expr;
|
2010-10-02 20:02:27 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-02 18:23:54 +02:00
|
|
|
NetExpr* normalize_variable_unpacked(const NetNet*net, list<NetExpr*>&indices)
|
|
|
|
|
{
|
|
|
|
|
const vector<netrange_t>&dims = net->unpacked_dims();
|
|
|
|
|
return normalize_variable_unpacked(*net, dims, indices);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr* normalize_variable_unpacked(const LineInfo&loc, const netsarray_t*stype, list<NetExpr*>&indices)
|
|
|
|
|
{
|
|
|
|
|
const vector<netrange_t>&dims = stype->static_dimensions();
|
|
|
|
|
return normalize_variable_unpacked(loc, dims, indices);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-12 22:07:34 +02:00
|
|
|
NetExpr* make_canonical_index(Design*des, NetScope*scope,
|
|
|
|
|
const LineInfo*loc,
|
|
|
|
|
const std::list<index_component_t>&src,
|
|
|
|
|
const netsarray_t*stype,
|
|
|
|
|
bool need_const)
|
|
|
|
|
{
|
|
|
|
|
NetExpr*canon_index = 0;
|
|
|
|
|
|
|
|
|
|
list<long> indices_const;
|
|
|
|
|
list<NetExpr*> indices_expr;
|
|
|
|
|
indices_flags flags;
|
|
|
|
|
indices_to_expressions(des, scope, loc,
|
|
|
|
|
src, src.size(),
|
2015-07-31 06:00:59 +02:00
|
|
|
need_const,
|
2014-10-21 18:12:02 +02:00
|
|
|
flags,
|
2014-09-12 22:07:34 +02:00
|
|
|
indices_expr, indices_const);
|
|
|
|
|
|
|
|
|
|
if (flags.undefined) {
|
|
|
|
|
cerr << loc->get_fileline() << ": warning: "
|
|
|
|
|
<< "ignoring undefined value array access." << endl;
|
|
|
|
|
|
|
|
|
|
} else if (flags.variable) {
|
|
|
|
|
canon_index = normalize_variable_unpacked(*loc, stype, indices_expr);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
canon_index = normalize_variable_unpacked(stype, indices_const);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return canon_index;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-07 07:05:17 +02:00
|
|
|
NetEConst* make_const_x(unsigned long wid)
|
|
|
|
|
{
|
|
|
|
|
verinum xxx (verinum::Vx, wid);
|
|
|
|
|
NetEConst*resx = new NetEConst(xxx);
|
2008-08-21 06:47:07 +02:00
|
|
|
return resx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetEConst* make_const_0(unsigned long wid)
|
|
|
|
|
{
|
|
|
|
|
verinum xxx (verinum::V0, wid);
|
|
|
|
|
NetEConst*resx = new NetEConst(xxx);
|
2008-06-07 07:05:17 +02:00
|
|
|
return resx;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-06 03:49:28 +01:00
|
|
|
NetEConst* make_const_val(unsigned long value)
|
|
|
|
|
{
|
|
|
|
|
verinum tmp (value, integer_width);
|
|
|
|
|
NetEConst*res = new NetEConst(tmp);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-26 02:10:26 +02:00
|
|
|
NetEConst* make_const_val_s(long value)
|
|
|
|
|
{
|
|
|
|
|
verinum tmp (value, integer_width);
|
|
|
|
|
tmp.has_sign(true);
|
|
|
|
|
NetEConst*res = new NetEConst(tmp);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-17 17:21:24 +02:00
|
|
|
NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid)
|
|
|
|
|
{
|
|
|
|
|
verinum xxx (verinum::Vx, wid);
|
|
|
|
|
NetConst*res = new NetConst(scope, scope->local_symbol(), xxx);
|
|
|
|
|
des->add_node(res);
|
|
|
|
|
|
2012-09-15 19:27:43 +02:00
|
|
|
netvector_t*sig_vec = new netvector_t(IVL_VT_LOGIC, wid-1, 0);
|
|
|
|
|
NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, sig_vec);
|
2008-08-17 17:21:24 +02:00
|
|
|
sig->local_flag(true);
|
|
|
|
|
|
|
|
|
|
connect(sig->pin(0), res->pin(0));
|
|
|
|
|
return sig;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-20 04:01:22 +01:00
|
|
|
NetNet* make_const_z(Design*des, NetScope*scope, unsigned long wid)
|
|
|
|
|
{
|
|
|
|
|
verinum xxx (verinum::Vz, wid);
|
|
|
|
|
NetConst*res = new NetConst(scope, scope->local_symbol(), xxx);
|
|
|
|
|
des->add_node(res);
|
|
|
|
|
|
|
|
|
|
netvector_t*sig_vec = new netvector_t(IVL_VT_LOGIC, wid-1, 0);
|
|
|
|
|
NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, sig_vec);
|
|
|
|
|
sig->local_flag(true);
|
|
|
|
|
|
|
|
|
|
connect(sig->pin(0), res->pin(0));
|
|
|
|
|
return sig;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-23 06:03:18 +02:00
|
|
|
NetExpr* condition_reduce(NetExpr*expr)
|
|
|
|
|
{
|
2009-02-14 03:25:54 +01:00
|
|
|
if (expr->expr_type() == IVL_VT_REAL) {
|
|
|
|
|
if (NetECReal *tmp = dynamic_cast<NetECReal*>(expr)) {
|
|
|
|
|
verinum::V res;
|
|
|
|
|
if (tmp->value().as_double() == 0.0) res = verinum::V0;
|
|
|
|
|
else res = verinum::V1;
|
|
|
|
|
verinum vres (res, 1, true);
|
|
|
|
|
NetExpr *rtn = new NetEConst(vres);
|
|
|
|
|
rtn->set_line(*expr);
|
|
|
|
|
delete expr;
|
|
|
|
|
return rtn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr *rtn = new NetEBComp('n', expr,
|
|
|
|
|
new NetECReal(verireal(0.0)));
|
|
|
|
|
rtn->set_line(*expr);
|
|
|
|
|
return rtn;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-23 06:03:18 +02:00
|
|
|
if (expr->expr_width() == 1)
|
|
|
|
|
return expr;
|
|
|
|
|
|
|
|
|
|
verinum zero (verinum::V0, expr->expr_width());
|
2011-02-26 23:59:52 +01:00
|
|
|
zero.has_sign(expr->has_sign());
|
2008-04-23 06:03:18 +02:00
|
|
|
|
|
|
|
|
NetEConst*ezero = new NetEConst(zero);
|
|
|
|
|
ezero->set_line(*expr);
|
|
|
|
|
|
|
|
|
|
NetEBComp*cmp = new NetEBComp('n', expr, ezero);
|
|
|
|
|
cmp->set_line(*expr);
|
2011-02-10 06:03:08 +01:00
|
|
|
cmp->cast_signed(false);
|
2008-04-23 06:03:18 +02:00
|
|
|
|
|
|
|
|
return cmp;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-16 18:31:59 +02:00
|
|
|
NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
|
|
|
|
|
int context_width, bool need_const, bool annotatable,
|
|
|
|
|
ivl_variable_type_t cast_type, bool force_unsigned)
|
2008-11-27 00:37:38 +01:00
|
|
|
{
|
2011-02-26 23:59:52 +01:00
|
|
|
PExpr::width_mode_t mode = PExpr::SIZED;
|
|
|
|
|
if ((context_width == -2) && !gn_strict_expr_width_flag)
|
|
|
|
|
mode = PExpr::EXPAND;
|
|
|
|
|
|
|
|
|
|
pe->test_width(des, scope, mode);
|
|
|
|
|
|
2020-12-28 04:03:44 +01:00
|
|
|
if (pe->expr_type() == IVL_VT_CLASS) {
|
|
|
|
|
cerr << pe->get_fileline() << ": Error: "
|
|
|
|
|
<< "Class/null r-value not allowed in this context." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
// Get the final expression width. If the expression is unsized,
|
|
|
|
|
// this may be different from the value returned by test_width().
|
|
|
|
|
unsigned expr_width = pe->expr_width();
|
|
|
|
|
|
|
|
|
|
// If context_width is positive, this is the RHS of an assignment,
|
|
|
|
|
// so the LHS width must also be included in the width calculation.
|
2014-02-27 20:20:20 +01:00
|
|
|
unsigned pos_context_width = context_width > 0 ? context_width : 0;
|
|
|
|
|
if ((pe->expr_type() != IVL_VT_REAL) && (expr_width < pos_context_width))
|
|
|
|
|
expr_width = pos_context_width;
|
2011-02-26 23:59:52 +01:00
|
|
|
|
2016-02-23 17:53:01 +01:00
|
|
|
// If this is the RHS of a compressed assignment, the LHS also
|
|
|
|
|
// affects the expression type (signed/unsigned).
|
|
|
|
|
if (force_unsigned)
|
|
|
|
|
pe->cast_signed(false);
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
if (debug_elaborate) {
|
2013-09-29 03:31:22 +02:00
|
|
|
cerr << pe->get_fileline() << ": elab_and_eval: test_width of "
|
2011-02-26 23:59:52 +01:00
|
|
|
<< *pe << endl;
|
2013-09-29 03:31:22 +02:00
|
|
|
cerr << pe->get_fileline() << ": : "
|
2011-02-26 23:59:52 +01:00
|
|
|
<< "returns type=" << pe->expr_type()
|
2014-01-15 23:26:53 +01:00
|
|
|
<< ", context_width=" << context_width
|
2011-02-26 23:59:52 +01:00
|
|
|
<< ", signed=" << pe->has_sign()
|
2014-01-15 23:26:53 +01:00
|
|
|
<< ", expr_width=" << expr_width
|
2013-04-07 13:23:45 +02:00
|
|
|
<< ", mode=" << PExpr::width_mode_name(mode) << endl;
|
2013-09-29 03:31:22 +02:00
|
|
|
cerr << pe->get_fileline() << ": : "
|
|
|
|
|
<< "cast_type=" << cast_type << endl;
|
2011-02-26 23:59:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we can get the same result using a smaller expression
|
|
|
|
|
// width, do so.
|
2014-10-21 18:12:02 +02:00
|
|
|
|
2014-08-25 21:27:22 +02:00
|
|
|
unsigned min_width = pe->min_width();
|
|
|
|
|
if ((min_width != UINT_MAX) && (pe->expr_type() != IVL_VT_REAL)
|
|
|
|
|
&& (pos_context_width > 0) && (expr_width > pos_context_width)) {
|
|
|
|
|
expr_width = max(min_width, pos_context_width);
|
2011-02-26 23:59:52 +01:00
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
2014-08-25 21:27:22 +02:00
|
|
|
cerr << pe->get_fileline() << ": : "
|
2011-02-26 23:59:52 +01:00
|
|
|
<< "pruned to width=" << expr_width << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-27 20:20:20 +01:00
|
|
|
if ((mode >= PExpr::LOSSLESS) && (expr_width > width_cap)
|
|
|
|
|
&& (expr_width > pos_context_width)) {
|
|
|
|
|
cerr << pe->get_fileline() << ": warning: excessive unsized "
|
|
|
|
|
<< "expression width detected." << endl;
|
|
|
|
|
cerr << pe->get_fileline() << ": : The expression width "
|
|
|
|
|
<< "is capped at " << width_cap << " bits." << endl;
|
|
|
|
|
expr_width = width_cap;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-27 12:08:33 +02:00
|
|
|
unsigned flags = PExpr::NO_FLAGS;
|
|
|
|
|
if (need_const)
|
|
|
|
|
flags |= PExpr::NEED_CONST;
|
2012-05-07 00:11:26 +02:00
|
|
|
if (annotatable)
|
|
|
|
|
flags |= PExpr::ANNOTATABLE;
|
2011-03-27 12:08:33 +02:00
|
|
|
|
2014-01-15 23:26:53 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << pe->get_fileline() << ": elab_and_eval: "
|
|
|
|
|
<< "Calculated width is " << expr_width << "." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-27 12:08:33 +02:00
|
|
|
NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, flags);
|
2011-02-26 23:59:52 +01:00
|
|
|
if (tmp == 0) return 0;
|
|
|
|
|
|
2013-02-25 21:32:56 +01:00
|
|
|
if ((cast_type != IVL_VT_NO_TYPE) && (cast_type != tmp->expr_type())) {
|
2019-10-06 19:03:37 +02:00
|
|
|
switch (tmp->expr_type()) {
|
|
|
|
|
case IVL_VT_BOOL:
|
|
|
|
|
case IVL_VT_LOGIC:
|
|
|
|
|
case IVL_VT_REAL:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
cerr << tmp->get_fileline() << ": error: "
|
|
|
|
|
"The expression '" << *pe << "' cannot be implicitly "
|
|
|
|
|
"cast to the target type." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
delete tmp;
|
|
|
|
|
return 0;
|
2019-09-07 15:35:19 +02:00
|
|
|
}
|
2013-02-25 21:32:56 +01:00
|
|
|
switch (cast_type) {
|
|
|
|
|
case IVL_VT_REAL:
|
|
|
|
|
tmp = cast_to_real(tmp);
|
|
|
|
|
break;
|
|
|
|
|
case IVL_VT_BOOL:
|
2014-02-27 20:20:20 +01:00
|
|
|
tmp = cast_to_int2(tmp, pos_context_width);
|
2013-02-25 21:32:56 +01:00
|
|
|
break;
|
|
|
|
|
case IVL_VT_LOGIC:
|
2014-02-27 20:20:20 +01:00
|
|
|
tmp = cast_to_int4(tmp, pos_context_width);
|
2013-02-25 21:32:56 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
eval_expr(tmp, context_width);
|
|
|
|
|
|
|
|
|
|
if (NetEConst*ce = dynamic_cast<NetEConst*>(tmp)) {
|
|
|
|
|
if ((mode >= PExpr::LOSSLESS) && (context_width < 0))
|
|
|
|
|
ce->trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tmp;
|
2008-11-27 00:37:38 +01:00
|
|
|
}
|
|
|
|
|
|
2013-09-29 23:48:42 +02:00
|
|
|
NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
|
|
|
|
|
ivl_type_t lv_net_type, bool need_const)
|
|
|
|
|
{
|
|
|
|
|
if (debug_elaborate) {
|
2020-12-28 03:53:34 +01:00
|
|
|
cerr << pe->get_fileline() << ": " << __func__ << ": "
|
2013-09-29 23:48:42 +02:00
|
|
|
<< "pe=" << *pe
|
|
|
|
|
<< ", lv_net_type=" << *lv_net_type << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Elaborate the expression using the more general
|
|
|
|
|
// elaborate_expr method.
|
|
|
|
|
unsigned flags = PExpr::NO_FLAGS;
|
|
|
|
|
if (need_const)
|
|
|
|
|
flags |= PExpr::NEED_CONST;
|
|
|
|
|
|
|
|
|
|
NetExpr*tmp = pe->elaborate_expr(des, scope, lv_net_type, flags);
|
2019-10-06 18:54:44 +02:00
|
|
|
if (tmp == 0) return 0;
|
|
|
|
|
|
|
|
|
|
ivl_variable_type_t cast_type = ivl_type_base(lv_net_type);
|
2022-04-15 18:23:41 +02:00
|
|
|
ivl_variable_type_t expr_type = tmp->expr_type();
|
2023-06-17 20:07:33 +02:00
|
|
|
|
|
|
|
|
bool compatible;
|
|
|
|
|
// For arrays we need strict type checking here. Long term strict type
|
|
|
|
|
// checking should be used for all expressions, but at the moment not
|
|
|
|
|
// all expressions do have a ivl_type_t attached to it.
|
|
|
|
|
if (dynamic_cast<const netuarray_t*>(lv_net_type)) {
|
|
|
|
|
if (tmp->net_type())
|
|
|
|
|
compatible = lv_net_type->type_compatible(tmp->net_type());
|
|
|
|
|
else
|
|
|
|
|
compatible = false;
|
|
|
|
|
} else if (cast_type == IVL_VT_NO_TYPE) {
|
|
|
|
|
compatible = true;
|
|
|
|
|
} else {
|
|
|
|
|
compatible = cast_type == expr_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!compatible) {
|
2019-10-06 18:54:44 +02:00
|
|
|
// Catch some special cases.
|
|
|
|
|
switch (cast_type) {
|
|
|
|
|
case IVL_VT_DARRAY:
|
|
|
|
|
case IVL_VT_QUEUE:
|
2022-04-15 18:23:41 +02:00
|
|
|
if ((expr_type == IVL_VT_DARRAY) || (expr_type == IVL_VT_QUEUE))
|
|
|
|
|
return tmp;
|
|
|
|
|
|
|
|
|
|
// This is needed to handle the special case of `'{}` which
|
|
|
|
|
// gets elaborated to NetENull.
|
2019-10-06 18:54:44 +02:00
|
|
|
if (dynamic_cast<PEAssignPattern*>(pe))
|
|
|
|
|
return tmp;
|
|
|
|
|
// fall through
|
|
|
|
|
case IVL_VT_STRING:
|
|
|
|
|
if (dynamic_cast<PEConcat*>(pe))
|
|
|
|
|
return tmp;
|
|
|
|
|
break;
|
|
|
|
|
case IVL_VT_CLASS:
|
|
|
|
|
if (dynamic_cast<PENull*>(pe))
|
|
|
|
|
return tmp;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cerr << tmp->get_fileline() << ": error: "
|
|
|
|
|
"The expression '" << *pe << "' cannot be implicitly "
|
|
|
|
|
"cast to the target type." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
delete tmp;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2013-09-29 23:48:42 +02:00
|
|
|
|
Add initial support for packed arrays/vector assignment pattern
SystemVerilog allows to use assignment patterns to assign a value to a
packed array.
This is similar to using a concatenation, with the difference that for
concatenations the values are evaluated in a self-determined context and
for assignment patterns they are evaluated in a context defined by the
element type of the packed array. This means that the value is for example
automatically width expanded or truncated if it does not have the same size
as the element type. Automatic type conversion is also done when allowed. E.g.
```
bit [3:0][3:0] x = '{1'b1, 32'h2, 3.0, "TEST"};
$display("%x", x); // -> 1234
```
Nested assignment patterns are also supported. E.g.
```
bit [1:0][3:0][3:0] x = '{'{1, 2, 3, 4.0}, '{5, 6, 7, 8}};
$display("%x", x); // -> 12345678
```
Add support for using assignment patterns as the right hand side value.
Since the complete type of the target variable is required to correctly
evaluate the assignment pattern it is handled as a special case in
`elab_rval_expression()`. For other types of expressions for packed values
only the total width of the target value is provided to the rvalue
elaboration function.
SystemVerilog also supports assignment patterns for the left hand side in
assignments. This is not yet supported.
Also not yet supported is specifying array elements by index, including
`default`.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2022-09-26 11:53:49 +02:00
|
|
|
if (lv_net_type->packed())
|
|
|
|
|
eval_expr(tmp, lv_net_type->packed_width());
|
|
|
|
|
else
|
|
|
|
|
eval_expr(tmp, -1);
|
|
|
|
|
|
2013-09-29 23:48:42 +02:00
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name,
|
2011-03-27 12:08:33 +02:00
|
|
|
unsigned arg_idx, PExpr*pe, bool need_const)
|
2001-02-11 03:15:52 +01:00
|
|
|
{
|
2011-02-26 23:59:52 +01:00
|
|
|
PExpr::width_mode_t mode = PExpr::SIZED;
|
|
|
|
|
pe->test_width(des, scope, mode);
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
2020-12-30 20:18:09 +01:00
|
|
|
cerr << pe->get_fileline() << ": " << __func__ << ": "
|
|
|
|
|
<< "test_width of " << name
|
|
|
|
|
<< " argument " << (arg_idx+1) << " " << *pe << endl;
|
2011-02-26 23:59:52 +01:00
|
|
|
cerr << pe->get_fileline() << ": "
|
|
|
|
|
<< "returns type=" << pe->expr_type()
|
|
|
|
|
<< ", width=" << pe->expr_width()
|
|
|
|
|
<< ", signed=" << pe->has_sign()
|
2013-04-07 13:23:45 +02:00
|
|
|
<< ", mode=" << PExpr::width_mode_name(mode) << endl;
|
2011-02-26 23:59:52 +01:00
|
|
|
}
|
|
|
|
|
|
2011-03-27 12:08:33 +02:00
|
|
|
unsigned flags = PExpr::SYS_TASK_ARG;
|
|
|
|
|
if (need_const)
|
|
|
|
|
flags |= PExpr::NEED_CONST;
|
|
|
|
|
|
|
|
|
|
NetExpr*tmp = pe->elaborate_expr(des, scope, pe->expr_width(), flags);
|
2008-03-08 03:51:50 +01:00
|
|
|
if (tmp == 0) return 0;
|
2001-02-11 03:15:52 +01:00
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
eval_expr(tmp, -1);
|
|
|
|
|
|
|
|
|
|
if (NetEConst*ce = dynamic_cast<NetEConst*>(tmp)) {
|
2012-05-01 00:05:42 +02:00
|
|
|
// For lossless/unsized constant expressions, we can now
|
|
|
|
|
// determine the exact width required to hold the result.
|
|
|
|
|
// But leave literal numbers exactly as the user supplied
|
|
|
|
|
// them.
|
2014-03-01 17:38:23 +01:00
|
|
|
if ((mode >= PExpr::LOSSLESS) && !dynamic_cast<PENumber*>(pe) && tmp->expr_width()>32)
|
2011-02-26 23:59:52 +01:00
|
|
|
ce->trim();
|
|
|
|
|
}
|
2001-02-11 03:15:52 +01:00
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-14 01:29:49 +02:00
|
|
|
bool evaluate_range(Design*des, NetScope*scope, const LineInfo*li,
|
|
|
|
|
const pform_range_t&range, long&index_l, long&index_r)
|
|
|
|
|
{
|
|
|
|
|
bool dimension_ok = true;
|
|
|
|
|
|
|
|
|
|
// Unsized and queue dimensions should be handled before calling
|
|
|
|
|
// this function. If we find them here, we are in a context where
|
|
|
|
|
// they are not allowed.
|
|
|
|
|
if (range.first == 0) {
|
|
|
|
|
cerr << li->get_fileline() << ": error: "
|
|
|
|
|
"An unsized dimension is not allowed here." << endl;
|
|
|
|
|
dimension_ok = false;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else if (dynamic_cast<PENull*>(range.first)) {
|
|
|
|
|
cerr << li->get_fileline() << ": error: "
|
|
|
|
|
"A queue dimension is not allowed here." << endl;
|
|
|
|
|
dimension_ok = false;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
} else {
|
|
|
|
|
NetExpr*texpr = elab_and_eval(des, scope, range.first, -1, true);
|
|
|
|
|
if (! eval_as_long(index_l, texpr)) {
|
|
|
|
|
cerr << range.first->get_fileline() << ": error: "
|
|
|
|
|
"Dimensions must be constant." << endl;
|
|
|
|
|
cerr << range.first->get_fileline() << " : "
|
|
|
|
|
<< (range.second ? "This MSB" : "This size")
|
|
|
|
|
<< " expression violates the rule: "
|
|
|
|
|
<< *range.first << endl;
|
|
|
|
|
dimension_ok = false;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
delete texpr;
|
|
|
|
|
|
|
|
|
|
if (range.second == 0) {
|
|
|
|
|
// This is a SystemVerilog [size] dimension. The IEEE
|
|
|
|
|
// standard does not allow this in a packed dimension,
|
|
|
|
|
// but we do. At least one commercial simulator does too.
|
|
|
|
|
if (!dimension_ok) {
|
|
|
|
|
// bail out
|
|
|
|
|
} else if (index_l > 0) {
|
2022-10-11 10:45:34 +02:00
|
|
|
index_r = index_l - 1;
|
|
|
|
|
index_l = 0;
|
2019-09-14 01:29:49 +02:00
|
|
|
} else {
|
|
|
|
|
cerr << range.first->get_fileline() << ": error: "
|
|
|
|
|
"Dimension size must be greater than zero." << endl;
|
|
|
|
|
cerr << range.first->get_fileline() << " : "
|
|
|
|
|
"This size expression violates the rule: "
|
|
|
|
|
<< *range.first << endl;
|
|
|
|
|
dimension_ok = false;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
texpr = elab_and_eval(des, scope, range.second, -1, true);
|
|
|
|
|
if (! eval_as_long(index_r, texpr)) {
|
|
|
|
|
cerr << range.second->get_fileline() << ": error: "
|
|
|
|
|
"Dimensions must be constant." << endl;
|
|
|
|
|
cerr << range.second->get_fileline() << " : "
|
|
|
|
|
"This LSB expression violates the rule: "
|
|
|
|
|
<< *range.second << endl;
|
|
|
|
|
dimension_ok = false;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
delete texpr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Error recovery */
|
|
|
|
|
if (!dimension_ok) {
|
|
|
|
|
index_l = 0;
|
|
|
|
|
index_r = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dimension_ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool evaluate_ranges(Design*des, NetScope*scope, const LineInfo*li,
|
2014-09-02 18:23:54 +02:00
|
|
|
vector<netrange_t>&llist,
|
|
|
|
|
const list<pform_range_t>&rlist)
|
|
|
|
|
{
|
2019-09-14 01:29:49 +02:00
|
|
|
bool dimensions_ok = true;
|
2014-09-02 18:23:54 +02:00
|
|
|
|
|
|
|
|
for (list<pform_range_t>::const_iterator cur = rlist.begin()
|
|
|
|
|
; cur != rlist.end() ; ++cur) {
|
2019-09-14 01:29:49 +02:00
|
|
|
long index_l, index_r;
|
|
|
|
|
dimensions_ok &= evaluate_range(des, scope, li, *cur, index_l, index_r);
|
|
|
|
|
llist.push_back(netrange_t(index_l, index_r));
|
2014-09-02 18:23:54 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-14 01:29:49 +02:00
|
|
|
return dimensions_ok;
|
2014-09-02 18:23:54 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
void eval_expr(NetExpr*&expr, int context_width)
|
2008-03-08 03:51:50 +01:00
|
|
|
{
|
|
|
|
|
assert(expr);
|
|
|
|
|
if (dynamic_cast<NetECReal*>(expr)) return;
|
|
|
|
|
|
2011-02-26 23:59:52 +01:00
|
|
|
NetExpr*tmp = expr->eval_tree();
|
2008-03-08 03:51:50 +01:00
|
|
|
if (tmp != 0) {
|
|
|
|
|
tmp->set_line(*expr);
|
|
|
|
|
delete expr;
|
|
|
|
|
expr = tmp;
|
|
|
|
|
}
|
2011-02-26 23:59:52 +01:00
|
|
|
|
|
|
|
|
if (context_width <= 0) return;
|
|
|
|
|
|
|
|
|
|
NetEConst *ce = dynamic_cast<NetEConst*>(expr);
|
|
|
|
|
if (ce == 0) return;
|
|
|
|
|
|
|
|
|
|
// The expression is a constant, so resize it if needed.
|
|
|
|
|
if (ce->expr_width() < (unsigned)context_width) {
|
|
|
|
|
expr = pad_to_width(expr, context_width, *expr);
|
2011-10-05 19:41:24 +02:00
|
|
|
} else if (ce->expr_width() > (unsigned)context_width) {
|
2011-02-26 23:59:52 +01:00
|
|
|
verinum value(ce->value(), context_width);
|
|
|
|
|
ce = new NetEConst(value);
|
|
|
|
|
ce->set_line(*expr);
|
|
|
|
|
delete expr;
|
|
|
|
|
expr = ce;
|
|
|
|
|
}
|
2008-03-08 03:51:50 +01:00
|
|
|
}
|
|
|
|
|
|
2013-09-01 03:48:32 +02:00
|
|
|
bool eval_as_long(long&value, const NetExpr*expr)
|
2007-07-18 03:07:34 +02:00
|
|
|
{
|
2013-09-01 03:48:32 +02:00
|
|
|
if (const NetEConst*tmp = dynamic_cast<const NetEConst*>(expr) ) {
|
2007-07-18 03:07:34 +02:00
|
|
|
value = tmp->value().as_long();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-01 03:48:32 +02:00
|
|
|
if (const NetECReal*rtmp = dynamic_cast<const NetECReal*>(expr)) {
|
2007-07-18 03:07:34 +02:00
|
|
|
value = rtmp->value().as_long();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-18 01:25:58 +02:00
|
|
|
bool eval_as_double(double&value, NetExpr*expr)
|
|
|
|
|
{
|
|
|
|
|
if (NetEConst*tmp = dynamic_cast<NetEConst*>(expr) ) {
|
2009-02-28 03:58:36 +01:00
|
|
|
value = tmp->value().as_double();
|
2008-05-18 01:25:58 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NetECReal*rtmp = dynamic_cast<NetECReal*>(expr)) {
|
|
|
|
|
value = rtmp->value().as_double();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-22 03:36:46 +02:00
|
|
|
/*
|
2008-08-28 18:01:43 +02:00
|
|
|
* At the parser level, a name component is a name with a collection
|
2008-06-22 03:36:46 +02:00
|
|
|
* of expressions. For example foo[N] is the name "foo" and the index
|
|
|
|
|
* expression "N". This function takes as input the name component and
|
2008-08-28 18:01:43 +02:00
|
|
|
* returns the path component name. It will evaluate the index
|
2008-06-22 03:36:46 +02:00
|
|
|
* expression if it is present.
|
|
|
|
|
*/
|
2012-08-26 04:28:02 +02:00
|
|
|
hname_t eval_path_component(Design*des, NetScope*scope,
|
|
|
|
|
const name_component_t&comp,
|
|
|
|
|
bool&error_flag)
|
2008-06-22 03:36:46 +02:00
|
|
|
{
|
2008-08-28 18:01:43 +02:00
|
|
|
// No index expression, so the path component is an undecorated
|
2008-06-22 03:36:46 +02:00
|
|
|
// name, for example "foo".
|
|
|
|
|
if (comp.index.empty())
|
|
|
|
|
return hname_t(comp.name);
|
|
|
|
|
|
2014-03-31 02:20:42 +02:00
|
|
|
vector<int> index_values;
|
2008-06-22 03:36:46 +02:00
|
|
|
|
2014-03-31 02:20:42 +02:00
|
|
|
for (list<index_component_t>::const_iterator cur = comp.index.begin()
|
|
|
|
|
; cur != comp.index.end() ; ++cur) {
|
|
|
|
|
const index_component_t&index = *cur;
|
2008-06-22 03:36:46 +02:00
|
|
|
|
2014-03-31 02:20:42 +02:00
|
|
|
if (index.sel != index_component_t::SEL_BIT) {
|
|
|
|
|
cerr << index.msb->get_fileline() << ": error: "
|
|
|
|
|
<< "Part select is not valid for this kind of object." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return hname_t(comp.name, 0);
|
|
|
|
|
}
|
2009-01-29 05:12:10 +01:00
|
|
|
|
2014-03-31 02:20:42 +02:00
|
|
|
// The parser will assure that path components will have only
|
|
|
|
|
// bit select index expressions. For example, "foo[n]" is OK,
|
|
|
|
|
// but "foo[n:m]" is not.
|
|
|
|
|
assert(index.sel == index_component_t::SEL_BIT);
|
2008-06-22 03:36:46 +02:00
|
|
|
|
2014-03-31 02:20:42 +02:00
|
|
|
// Evaluate the bit select to get a number.
|
|
|
|
|
NetExpr*tmp = elab_and_eval(des, scope, index.msb, -1);
|
|
|
|
|
ivl_assert(*index.msb, tmp);
|
2008-06-22 03:36:46 +02:00
|
|
|
|
2014-03-31 02:20:42 +02:00
|
|
|
if (NetEConst*ctmp = dynamic_cast<NetEConst*>(tmp)) {
|
|
|
|
|
index_values.push_back(ctmp->value().as_long());
|
|
|
|
|
delete ctmp;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2012-08-20 02:27:48 +02:00
|
|
|
#if 1
|
2014-03-31 02:20:42 +02:00
|
|
|
// Darn, the expression doesn't evaluate to a constant. That's
|
|
|
|
|
// an error to be reported. And make up a fake index value to
|
|
|
|
|
// return to the caller.
|
|
|
|
|
cerr << index.msb->get_fileline() << ": error: "
|
|
|
|
|
<< "Scope index expression is not constant: "
|
|
|
|
|
<< *index.msb << endl;
|
|
|
|
|
des->errors += 1;
|
2012-08-20 02:27:48 +02:00
|
|
|
#endif
|
2014-03-31 02:20:42 +02:00
|
|
|
error_flag = true;
|
|
|
|
|
|
|
|
|
|
delete tmp;
|
|
|
|
|
}
|
2008-06-22 03:36:46 +02:00
|
|
|
|
2014-03-31 02:20:42 +02:00
|
|
|
return hname_t(comp.name, index_values);
|
2008-06-22 03:36:46 +02:00
|
|
|
}
|
|
|
|
|
|
2007-06-02 05:42:12 +02:00
|
|
|
std::list<hname_t> eval_scope_path(Design*des, NetScope*scope,
|
|
|
|
|
const pform_name_t&path)
|
|
|
|
|
{
|
2012-08-20 02:27:48 +02:00
|
|
|
bool path_error_flag = false;
|
2007-06-02 05:42:12 +02:00
|
|
|
list<hname_t> res;
|
|
|
|
|
|
|
|
|
|
typedef pform_name_t::const_iterator pform_path_it;
|
|
|
|
|
|
2010-10-23 23:57:59 +02:00
|
|
|
for (pform_path_it cur = path.begin() ; cur != path.end(); ++ cur ) {
|
2007-06-02 05:42:12 +02:00
|
|
|
const name_component_t&comp = *cur;
|
2012-08-20 02:27:48 +02:00
|
|
|
res.push_back( eval_path_component(des,scope,comp,path_error_flag) );
|
2007-06-02 05:42:12 +02:00
|
|
|
}
|
2012-08-20 02:27:48 +02:00
|
|
|
#if 0
|
|
|
|
|
if (path_error_flag) {
|
|
|
|
|
cerr << "XXXXX: Errors evaluating path " << path << endl;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2007-06-02 05:42:12 +02:00
|
|
|
return res;
|
|
|
|
|
}
|
2001-02-11 03:15:52 +01:00
|
|
|
|
2008-02-22 23:51:53 +01:00
|
|
|
/*
|
|
|
|
|
* Human readable version of op. Used in elaboration error messages.
|
|
|
|
|
*/
|
2009-02-14 03:25:54 +01:00
|
|
|
const char *human_readable_op(const char op, bool unary)
|
2008-02-22 23:51:53 +01:00
|
|
|
{
|
|
|
|
|
const char *type;
|
|
|
|
|
switch (op) {
|
2008-09-30 22:22:57 +02:00
|
|
|
case '~': type = "~"; break; // Negation
|
2008-02-22 23:51:53 +01:00
|
|
|
|
2008-12-19 06:33:31 +01:00
|
|
|
case '+': type = "+"; break;
|
|
|
|
|
case '-': type = "-"; break;
|
2008-12-20 02:17:39 +01:00
|
|
|
case '*': type = "*"; break;
|
|
|
|
|
case '/': type = "/"; break;
|
2009-05-14 18:32:15 +02:00
|
|
|
case '%': type = "%"; break;
|
2008-12-20 02:17:39 +01:00
|
|
|
|
|
|
|
|
case '<': type = "<"; break;
|
|
|
|
|
case '>': type = ">"; break;
|
|
|
|
|
case 'L': type = "<="; break;
|
|
|
|
|
case 'G': type = ">="; break;
|
2010-10-02 20:02:27 +02:00
|
|
|
|
2008-09-30 22:22:57 +02:00
|
|
|
case '^': type = "^"; break; // XOR
|
|
|
|
|
case 'X': type = "~^"; break; // XNOR
|
|
|
|
|
case '&': type = "&"; break; // Bitwise AND
|
|
|
|
|
case 'A': type = "~&"; break; // NAND (~&)
|
|
|
|
|
case '|': type = "|"; break; // Bitwise OR
|
|
|
|
|
case 'O': type = "~|"; break; // NOR
|
2008-02-22 23:51:53 +01:00
|
|
|
|
2020-07-08 08:23:39 +02:00
|
|
|
case '!': type = "!"; break; // Logical NOT
|
|
|
|
|
case 'a': type = "&&"; break; // Logical AND
|
|
|
|
|
case 'o': type = "||"; break; // Logical OR
|
|
|
|
|
case 'q': type = "->"; break; // Logical implication
|
|
|
|
|
case 'Q': type = "<->"; break; // Logical equivalence
|
2008-02-22 23:51:53 +01:00
|
|
|
|
2008-12-20 02:17:39 +01:00
|
|
|
case 'e': type = "=="; break;
|
|
|
|
|
case 'n': type = "!="; break;
|
2008-09-30 22:22:57 +02:00
|
|
|
case 'E': type = "==="; break; // Case equality
|
2009-02-14 03:25:54 +01:00
|
|
|
case 'N':
|
|
|
|
|
if (unary) type = "~|"; // NOR
|
|
|
|
|
else type = "!=="; // Case inequality
|
|
|
|
|
break;
|
2017-11-18 04:32:09 +01:00
|
|
|
case 'w': type = "==?"; break; // Wild equality
|
|
|
|
|
case 'W': type = "!=?"; break; // Wild inequality
|
2008-02-22 23:51:53 +01:00
|
|
|
|
2008-09-30 22:22:57 +02:00
|
|
|
case 'l': type = "<<(<)"; break; // Left shifts
|
|
|
|
|
case 'r': type = ">>"; break; // Logical right shift
|
|
|
|
|
case 'R': type = ">>>"; break; // Arithmetic right shift
|
2008-02-22 23:51:53 +01:00
|
|
|
|
2008-11-28 23:40:25 +01:00
|
|
|
case 'p': type = "**"; break; // Power
|
2011-08-06 23:54:38 +02:00
|
|
|
|
|
|
|
|
case 'i':
|
|
|
|
|
case 'I': type = "++"; break; /* increment */
|
|
|
|
|
case 'd':
|
|
|
|
|
case 'D': type = "--"; break; /* decrement */
|
|
|
|
|
|
2008-09-30 22:22:57 +02:00
|
|
|
default:
|
2009-12-10 22:58:50 +01:00
|
|
|
type = "???";
|
2008-09-30 22:22:57 +02:00
|
|
|
assert(0);
|
2008-02-22 23:51:53 +01:00
|
|
|
}
|
|
|
|
|
return type;
|
|
|
|
|
}
|
2008-03-08 03:51:50 +01:00
|
|
|
|
|
|
|
|
const_bool const_logical(const NetExpr*expr)
|
|
|
|
|
{
|
|
|
|
|
switch (expr->expr_type()) {
|
|
|
|
|
case IVL_VT_REAL: {
|
|
|
|
|
const NetECReal*val = dynamic_cast<const NetECReal*> (expr);
|
|
|
|
|
if (val == 0) return C_NON;
|
|
|
|
|
if (val->value().as_double() == 0.0) return C_0;
|
|
|
|
|
else return C_1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case IVL_VT_BOOL:
|
|
|
|
|
case IVL_VT_LOGIC: {
|
|
|
|
|
const NetEConst*val = dynamic_cast<const NetEConst*> (expr);
|
|
|
|
|
if (val == 0) return C_NON;
|
|
|
|
|
verinum cval = val->value();
|
|
|
|
|
const_bool res = C_0;
|
|
|
|
|
for (unsigned idx = 0; idx < cval.len(); idx += 1) {
|
|
|
|
|
switch (cval.get(idx)) {
|
|
|
|
|
case verinum::V1:
|
2010-11-01 22:37:06 +01:00
|
|
|
return C_1;
|
2008-03-08 03:51:50 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case verinum::V0:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if (res == C_0) res = C_X;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return C_NON;
|
|
|
|
|
}
|
2010-06-19 01:03:17 +02:00
|
|
|
|
|
|
|
|
uint64_t get_scaled_time_from_real(Design*des, NetScope*scope, NetECReal*val)
|
|
|
|
|
{
|
|
|
|
|
verireal fn = val->value();
|
|
|
|
|
|
|
|
|
|
int shift = scope->time_unit() - scope->time_precision();
|
2023-04-13 22:00:34 +02:00
|
|
|
ivl_assert(*scope, shift >= 0);
|
2010-06-19 01:03:17 +02:00
|
|
|
int64_t delay = fn.as_long64(shift);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
shift = scope->time_precision() - des->get_precision();
|
2023-04-13 22:00:34 +02:00
|
|
|
ivl_assert(*scope, shift >= 0);
|
2010-06-19 01:03:17 +02:00
|
|
|
for (int lp = 0; lp < shift; lp += 1) delay *= 10;
|
|
|
|
|
|
|
|
|
|
return delay;
|
|
|
|
|
}
|
2011-04-04 02:21:43 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function looks at the NetNet signal to see if there are any
|
|
|
|
|
* NetPartSelect::PV nodes driving this signal. If so, See if they can
|
|
|
|
|
* be collapsed into a single concatenation.
|
|
|
|
|
*/
|
|
|
|
|
void collapse_partselect_pv_to_concat(Design*des, NetNet*sig)
|
|
|
|
|
{
|
|
|
|
|
NetScope*scope = sig->scope();
|
|
|
|
|
vector<NetPartSelect*> ps_map (sig->vector_width());
|
|
|
|
|
|
|
|
|
|
Nexus*nex = sig->pin(0).nexus();
|
|
|
|
|
|
|
|
|
|
for (Link*cur = nex->first_nlink(); cur ; cur = cur->next_nlink()) {
|
|
|
|
|
NetPins*obj;
|
|
|
|
|
unsigned obj_pin;
|
|
|
|
|
cur->cur_link(obj, obj_pin);
|
|
|
|
|
|
|
|
|
|
// Look for NetPartSelect devices, where this signal is
|
|
|
|
|
// connected to pin 1 of a NetPartSelect::PV.
|
|
|
|
|
NetPartSelect*ps_obj = dynamic_cast<NetPartSelect*> (obj);
|
|
|
|
|
if (ps_obj == 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (ps_obj->dir() != NetPartSelect::PV)
|
|
|
|
|
continue;
|
|
|
|
|
if (obj_pin != 1)
|
|
|
|
|
continue;
|
|
|
|
|
|
2011-04-04 02:41:52 +02:00
|
|
|
// Don't support overrun selects here.
|
|
|
|
|
if (ps_obj->base()+ps_obj->width() > ps_map.size())
|
|
|
|
|
continue;
|
|
|
|
|
|
2011-04-04 02:21:43 +02:00
|
|
|
ivl_assert(*ps_obj, ps_obj->base() < ps_map.size());
|
|
|
|
|
ps_map[ps_obj->base()] = ps_obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check the collected NetPartSelect::PV objects to see if
|
|
|
|
|
// they cover the vector.
|
|
|
|
|
unsigned idx = 0;
|
|
|
|
|
unsigned device_count = 0;
|
|
|
|
|
while (idx < ps_map.size()) {
|
|
|
|
|
NetPartSelect*ps_obj = ps_map[idx];
|
|
|
|
|
if (ps_obj == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
idx += ps_obj->width();
|
|
|
|
|
device_count += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivl_assert(*sig, idx == ps_map.size());
|
|
|
|
|
|
2013-02-14 04:12:54 +01:00
|
|
|
/* The vlog95 and possibly other code generators do not want
|
|
|
|
|
* to have a group of part selects turned into a transparent
|
|
|
|
|
* concatenation. */
|
|
|
|
|
if (disable_concatz_generation) {
|
|
|
|
|
// HERE: If the part selects have matching strengths then we can use
|
|
|
|
|
// a normal concat with a buf-Z after if the strengths are not
|
|
|
|
|
// both strong. We would ideally delete any buf-Z driving the
|
|
|
|
|
// concat, but that is not required for the vlog95 generator.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-04 02:21:43 +02:00
|
|
|
// Ah HAH! The NetPartSelect::PV objects exactly cover the
|
|
|
|
|
// target signal. We can replace all of them with a single
|
|
|
|
|
// concatenation.
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << sig->get_fileline() << ": debug: "
|
|
|
|
|
<< "Collapse " << device_count
|
|
|
|
|
<< " NetPartSelect::PV devices into a concatenation." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetConcat*cat = new NetConcat(scope, scope->local_symbol(),
|
2013-02-02 19:44:16 +01:00
|
|
|
ps_map.size(), device_count,
|
|
|
|
|
true);
|
2011-04-04 02:21:43 +02:00
|
|
|
des->add_node(cat);
|
|
|
|
|
cat->set_line(*sig);
|
|
|
|
|
|
|
|
|
|
connect(cat->pin(0), sig->pin(0));
|
|
|
|
|
|
|
|
|
|
idx = 0;
|
|
|
|
|
unsigned concat_position = 1;
|
|
|
|
|
while (idx < ps_map.size()) {
|
|
|
|
|
assert(ps_map[idx]);
|
|
|
|
|
NetPartSelect*ps_obj = ps_map[idx];
|
|
|
|
|
connect(cat->pin(concat_position), ps_obj->pin(0));
|
|
|
|
|
concat_position += 1;
|
|
|
|
|
idx += ps_obj->width();
|
|
|
|
|
delete ps_obj;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-02-11 02:17:59 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Evaluate the prefix indices. All but the final index in a
|
|
|
|
|
* chain of indices must be a single value and must evaluate
|
|
|
|
|
* to constants at compile time. For example:
|
|
|
|
|
* [x] - OK
|
|
|
|
|
* [1][2][x] - OK
|
|
|
|
|
* [1][x:y] - OK
|
|
|
|
|
* [2:0][x] - BAD
|
|
|
|
|
* [y][x] - BAD
|
|
|
|
|
* Leave the last index for special handling.
|
|
|
|
|
*/
|
|
|
|
|
bool evaluate_index_prefix(Design*des, NetScope*scope,
|
|
|
|
|
list<long>&prefix_indices,
|
|
|
|
|
const list<index_component_t>&indices)
|
|
|
|
|
{
|
|
|
|
|
list<index_component_t>::const_iterator icur = indices.begin();
|
|
|
|
|
for (size_t idx = 0 ; (idx+1) < indices.size() ; idx += 1, ++icur) {
|
|
|
|
|
assert(icur != indices.end());
|
2020-02-15 22:20:20 +01:00
|
|
|
if (icur->sel != index_component_t::SEL_BIT) {
|
|
|
|
|
cerr << icur->msb->get_fileline() << ": error: "
|
|
|
|
|
"All but the final index in a chain of indices must be "
|
|
|
|
|
"a single value, not a range." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-02-11 02:17:59 +01:00
|
|
|
NetExpr*texpr = elab_and_eval(des, scope, icur->msb, -1, true);
|
2012-02-12 20:16:31 +01:00
|
|
|
|
2012-02-11 02:17:59 +01:00
|
|
|
long tmp;
|
2012-02-12 20:16:31 +01:00
|
|
|
if (texpr == 0 || !eval_as_long(tmp, texpr)) {
|
2012-02-11 02:17:59 +01:00
|
|
|
cerr << icur->msb->get_fileline() << ": error: "
|
2012-02-12 20:16:31 +01:00
|
|
|
"Array index expressions must be constant here." << endl;
|
2012-02-11 02:17:59 +01:00
|
|
|
des->errors += 1;
|
2012-02-12 20:16:31 +01:00
|
|
|
return false;
|
2012-02-11 02:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
2016-02-15 15:12:40 +01:00
|
|
|
prefix_indices.push_back(tmp);
|
2012-02-11 02:17:59 +01:00
|
|
|
delete texpr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2012-08-06 01:28:40 +02:00
|
|
|
|
2012-08-20 02:27:48 +02:00
|
|
|
/*
|
|
|
|
|
* Evaluate the indices. The chain of indices are applied to the
|
|
|
|
|
* packed indices of a NetNet to generate a canonical expression to
|
|
|
|
|
* replace the exprs.
|
|
|
|
|
*/
|
|
|
|
|
NetExpr*collapse_array_exprs(Design*des, NetScope*scope,
|
|
|
|
|
const LineInfo*loc, NetNet*net,
|
|
|
|
|
const list<index_component_t>&indices)
|
|
|
|
|
{
|
|
|
|
|
// First elaborate all the expressions as far as possible.
|
|
|
|
|
list<NetExpr*> exprs;
|
|
|
|
|
list<long> exprs_const;
|
2013-05-18 20:21:37 +02:00
|
|
|
indices_flags flags;
|
2012-11-13 03:13:41 +01:00
|
|
|
indices_to_expressions(des, scope, loc, indices,
|
|
|
|
|
net->packed_dimensions(),
|
2015-07-31 06:00:59 +02:00
|
|
|
false, flags, exprs, exprs_const);
|
2012-08-20 02:27:48 +02:00
|
|
|
ivl_assert(*loc, exprs.size() == net->packed_dimensions());
|
|
|
|
|
|
|
|
|
|
// Special Case: there is only 1 packed dimension, so the
|
|
|
|
|
// single expression should already be naturally canonical.
|
|
|
|
|
if (net->slice_width(1) == 1) {
|
|
|
|
|
return *exprs.begin();
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-30 00:13:45 +02:00
|
|
|
const std::vector<netrange_t>&pdims = net->packed_dims();
|
|
|
|
|
std::vector<netrange_t>::const_iterator pcur = pdims.begin();
|
2012-08-20 02:27:48 +02:00
|
|
|
|
|
|
|
|
list<NetExpr*>::iterator ecur = exprs.begin();
|
|
|
|
|
NetExpr* base = 0;
|
|
|
|
|
for (size_t idx = 0 ; idx < net->packed_dimensions() ; idx += 1, ++pcur, ++ecur) {
|
|
|
|
|
unsigned cur_slice_width = net->slice_width(idx+1);
|
2014-12-05 19:43:39 +01:00
|
|
|
long lsb = pcur->get_lsb();
|
|
|
|
|
long msb = pcur->get_msb();
|
2012-08-20 02:27:48 +02:00
|
|
|
// This normalizes the expression of this index based on
|
|
|
|
|
// the msb/lsb values.
|
2014-12-05 19:43:39 +01:00
|
|
|
NetExpr*tmp = normalize_variable_base(*ecur, msb, lsb,
|
|
|
|
|
cur_slice_width, msb > lsb);
|
2012-08-20 02:27:48 +02:00
|
|
|
|
|
|
|
|
// If this slice has width, then scale it.
|
|
|
|
|
if (net->slice_width(idx+1) != 1) {
|
|
|
|
|
unsigned min_wid = tmp->expr_width();
|
|
|
|
|
if (num_bits(cur_slice_width) >= min_wid) {
|
|
|
|
|
min_wid = num_bits(cur_slice_width)+1;
|
|
|
|
|
tmp = pad_to_width(tmp, min_wid, *loc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp = make_mult_expr(tmp, cur_slice_width);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now add it to the position we've accumulated so far.
|
|
|
|
|
if (base) {
|
|
|
|
|
base = make_add_expr(loc, base, tmp);
|
|
|
|
|
} else {
|
|
|
|
|
base = tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return base;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-06 01:28:40 +02:00
|
|
|
/*
|
|
|
|
|
* Given a list of indices, treat them as packed indices and convert
|
|
|
|
|
* them to an expression that normalizes the list to a single index
|
2014-01-29 00:50:27 +01:00
|
|
|
* expression over a canonical equivalent 1-dimensional array.
|
2012-08-06 01:28:40 +02:00
|
|
|
*/
|
|
|
|
|
NetExpr*collapse_array_indices(Design*des, NetScope*scope, NetNet*net,
|
|
|
|
|
const list<index_component_t>&indices)
|
|
|
|
|
{
|
|
|
|
|
list<long>prefix_indices;
|
|
|
|
|
bool rc = evaluate_index_prefix(des, scope, prefix_indices, indices);
|
|
|
|
|
assert(rc);
|
|
|
|
|
|
|
|
|
|
const index_component_t&back_index = indices.back();
|
|
|
|
|
assert(back_index.sel == index_component_t::SEL_BIT);
|
|
|
|
|
assert(back_index.msb && !back_index.lsb);
|
|
|
|
|
|
|
|
|
|
NetExpr*base = elab_and_eval(des, scope, back_index.msb, -1, true);
|
|
|
|
|
|
|
|
|
|
NetExpr*res = normalize_variable_bit_base(prefix_indices, base, net);
|
2012-08-20 02:27:48 +02:00
|
|
|
|
|
|
|
|
eval_expr(res, -1);
|
2012-08-06 01:28:40 +02:00
|
|
|
return res;
|
|
|
|
|
}
|
2014-03-23 04:50:47 +01:00
|
|
|
|
2023-03-05 18:15:59 +01:00
|
|
|
|
|
|
|
|
static void assign_unpacked_with_bufz_dim(Design *des, NetScope *scope,
|
|
|
|
|
const LineInfo *loc,
|
|
|
|
|
NetNet *lval, NetNet *rval,
|
|
|
|
|
const std::vector<long> &stride,
|
|
|
|
|
unsigned int dim = 0,
|
|
|
|
|
unsigned int idx_l = 0,
|
|
|
|
|
unsigned int idx_r = 0)
|
|
|
|
|
{
|
|
|
|
|
int inc_l, inc_r;
|
|
|
|
|
bool up_l, up_r;
|
|
|
|
|
|
|
|
|
|
const auto &l_dims = lval->unpacked_dims();
|
|
|
|
|
const auto &r_dims = rval->unpacked_dims();
|
|
|
|
|
|
|
|
|
|
up_l = l_dims[dim].get_msb() < l_dims[dim].get_lsb();
|
|
|
|
|
up_r = r_dims[dim].get_msb() < r_dims[dim].get_lsb();
|
|
|
|
|
|
|
|
|
|
inc_l = inc_r = stride[dim];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Arrays dimensions get connected left-to-right. This means if the
|
|
|
|
|
* left-to-right order differs for a particular dimension between the two
|
|
|
|
|
* arrays the elements for that dimension will get connected in reverse
|
|
|
|
|
* order.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!up_l) {
|
|
|
|
|
/* Go to the last element and count down */
|
|
|
|
|
idx_l += inc_l * (l_dims[dim].width() - 1);
|
|
|
|
|
inc_l = -inc_l;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!up_r) {
|
|
|
|
|
/* Go to the last element and count down */
|
|
|
|
|
idx_r += inc_r * (r_dims[dim].width() - 1);
|
|
|
|
|
inc_r = -inc_r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (unsigned int idx = 0; idx < l_dims[dim].width(); idx++) {
|
|
|
|
|
if (dim == l_dims.size() - 1) {
|
|
|
|
|
NetBUFZ *driver = new NetBUFZ(scope, scope->local_symbol(),
|
|
|
|
|
lval->vector_width(), false);
|
|
|
|
|
driver->set_line(*loc);
|
|
|
|
|
des->add_node(driver);
|
|
|
|
|
|
|
|
|
|
connect(lval->pin(idx_l), driver->pin(0));
|
|
|
|
|
connect(driver->pin(1), rval->pin(idx_r));
|
|
|
|
|
} else {
|
|
|
|
|
assign_unpacked_with_bufz_dim(des, scope, loc, lval, rval,
|
|
|
|
|
stride, dim + 1, idx_l, idx_r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idx_l += inc_l;
|
|
|
|
|
idx_r += inc_r;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-23 04:50:47 +01:00
|
|
|
void assign_unpacked_with_bufz(Design*des, NetScope*scope,
|
|
|
|
|
const LineInfo*loc,
|
|
|
|
|
NetNet*lval, NetNet*rval)
|
|
|
|
|
{
|
|
|
|
|
ivl_assert(*loc, lval->pin_count()==rval->pin_count());
|
|
|
|
|
|
2023-03-05 18:15:59 +01:00
|
|
|
const auto &dims = lval->unpacked_dims();
|
|
|
|
|
vector<long> stride(dims.size());
|
2014-03-23 04:50:47 +01:00
|
|
|
|
2023-03-05 18:15:59 +01:00
|
|
|
make_strides(dims, stride);
|
|
|
|
|
assign_unpacked_with_bufz_dim(des, scope, loc, lval, rval, stride);
|
2014-03-23 04:50:47 +01:00
|
|
|
}
|
|
|
|
|
|
2014-05-25 05:08:48 +02:00
|
|
|
/*
|
|
|
|
|
* synthesis sometimes needs to unpack assignment to a part
|
|
|
|
|
* select. That looks like this:
|
|
|
|
|
*
|
|
|
|
|
* foo[N] <= <expr> ;
|
|
|
|
|
*
|
|
|
|
|
* The NetAssignBase::synth_async() method will turn that into a
|
|
|
|
|
* netlist like this:
|
|
|
|
|
*
|
|
|
|
|
* NetAssignBase(PV) --> base()==<N>
|
|
|
|
|
* (0) (1)
|
|
|
|
|
* | |
|
|
|
|
|
* v v
|
|
|
|
|
* <expr> foo
|
|
|
|
|
*
|
|
|
|
|
* This search will return a pointer to the NetAssignBase(PV) object,
|
|
|
|
|
* but only if it matches this pattern.
|
|
|
|
|
*/
|
|
|
|
|
NetPartSelect* detect_partselect_lval(Link&pin)
|
|
|
|
|
{
|
|
|
|
|
NetPartSelect*found_ps = 0;
|
|
|
|
|
|
|
|
|
|
Nexus*nex = pin.nexus();
|
|
|
|
|
for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) {
|
|
|
|
|
NetPins*obj;
|
|
|
|
|
unsigned obj_pin;
|
|
|
|
|
cur->cur_link(obj, obj_pin);
|
|
|
|
|
|
2014-05-26 03:33:26 +02:00
|
|
|
// Skip NexusSet objects.
|
|
|
|
|
if (obj == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2014-05-25 05:08:48 +02:00
|
|
|
// NetNet pins have no effect on this search.
|
|
|
|
|
if (dynamic_cast<NetNet*> (obj))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (NetPartSelect*ps = dynamic_cast<NetPartSelect*> (obj)) {
|
|
|
|
|
|
|
|
|
|
// If this is the input side of a NetPartSelect, skip.
|
|
|
|
|
if (ps->pin(obj_pin).get_dir()==Link::INPUT)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Oops, driven by the wrong size of a
|
|
|
|
|
// NetPartSelect, so this is not going to work out.
|
|
|
|
|
if (ps->dir()==NetPartSelect::VP)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
// So now we know this is a NetPartSelect::PV. It
|
|
|
|
|
// is a candidate for our part-select assign. If
|
|
|
|
|
// we already have a candidate, then give up.
|
|
|
|
|
if (found_ps)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
// This is our candidate. Carry on.
|
|
|
|
|
found_ps = ps;
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If this is a driver to the Nexus that is not a
|
|
|
|
|
// NetPartSelect device. This cannot happen to
|
|
|
|
|
// part selected lval nets, so quit now.
|
|
|
|
|
if (obj->pin(obj_pin).get_dir() == Link::OUTPUT)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return found_ps;
|
|
|
|
|
}
|
2014-09-07 01:26:08 +02:00
|
|
|
|
|
|
|
|
const netclass_t* find_class_containing_scope(const LineInfo&loc, const NetScope*scope)
|
|
|
|
|
{
|
|
|
|
|
while (scope && scope->type() != NetScope::CLASS)
|
|
|
|
|
scope = scope->parent();
|
|
|
|
|
|
|
|
|
|
if (scope == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
const netclass_t*found_in = scope->class_def();
|
|
|
|
|
ivl_assert(loc, found_in);
|
|
|
|
|
return found_in;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Find the scope that contains this scope, that is the method for a
|
|
|
|
|
* class scope. Look for the scope whose PARENT is the scope for a
|
|
|
|
|
* class. This is going to be a method.
|
|
|
|
|
*/
|
|
|
|
|
NetScope* find_method_containing_scope(const LineInfo&, NetScope*scope)
|
|
|
|
|
{
|
|
|
|
|
NetScope*up = scope->parent();
|
|
|
|
|
|
|
|
|
|
while (up && up->type() != NetScope::CLASS) {
|
|
|
|
|
scope = up;
|
|
|
|
|
up = up->parent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (up == 0) return 0;
|
|
|
|
|
|
|
|
|
|
// Should I check if this scope is a TASK or FUNC?
|
|
|
|
|
|
|
|
|
|
return scope;
|
|
|
|
|
}
|
2017-11-05 10:35:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Print a warning if we find a mixture of default and explicit timescale
|
|
|
|
|
* based delays in the design, since this is likely an error.
|
|
|
|
|
*/
|
|
|
|
|
void check_for_inconsistent_delays(NetScope*scope)
|
|
|
|
|
{
|
|
|
|
|
static bool used_implicit_timescale = false;
|
|
|
|
|
static bool used_explicit_timescale = false;
|
|
|
|
|
static bool display_ts_dly_warning = true;
|
|
|
|
|
|
|
|
|
|
if (scope->time_from_timescale())
|
|
|
|
|
used_explicit_timescale = true;
|
|
|
|
|
else
|
|
|
|
|
used_implicit_timescale = true;
|
|
|
|
|
|
|
|
|
|
if (display_ts_dly_warning &&
|
|
|
|
|
used_explicit_timescale &&
|
|
|
|
|
used_implicit_timescale) {
|
|
|
|
|
if (gn_system_verilog()) {
|
|
|
|
|
cerr << "warning: Found both default and explicit "
|
|
|
|
|
"timescale based delays. Use" << endl;
|
|
|
|
|
cerr << " : -Wtimescale to find the design "
|
|
|
|
|
"element(s) with no explicit" << endl;
|
|
|
|
|
cerr << " : timescale." << endl;
|
|
|
|
|
} else {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-25 03:12:06 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Calculate the bit vector range for a parameter, from the type of the
|
|
|
|
|
* parameter. This is expecting that the type is a vector type. The parameter
|
|
|
|
|
* is presumably declared something like this:
|
|
|
|
|
*
|
|
|
|
|
* parameter [4:1] foo = <value>;
|
|
|
|
|
*
|
|
|
|
|
* In this case, the par_type is a netvector with a single dimension. The
|
|
|
|
|
* par_msv gets 4, and par_lsv get 1. The caller uses these values to
|
|
|
|
|
* interpret things like bit selects.
|
|
|
|
|
*/
|
|
|
|
|
bool calculate_param_range(const LineInfo&line, ivl_type_t par_type,
|
|
|
|
|
long&par_msv, long&par_lsv, long length)
|
|
|
|
|
{
|
|
|
|
|
const netvector_t*vector_type = dynamic_cast<const netvector_t*> (par_type);
|
|
|
|
|
if (vector_type == 0) {
|
|
|
|
|
// If the parameter doesn't have an explicit range, then
|
|
|
|
|
// just return range values of [length-1:0].
|
|
|
|
|
par_msv = length-1;
|
|
|
|
|
par_lsv = 0;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivl_assert(line, vector_type->packed());
|
|
|
|
|
const std::vector<netrange_t>& packed_dims = vector_type->packed_dims();
|
|
|
|
|
|
|
|
|
|
// This is a netvector_t with 0 dimensions, then the parameter was
|
|
|
|
|
// declared with a statement like this:
|
|
|
|
|
//
|
|
|
|
|
// parameter signed foo = <value>;
|
|
|
|
|
//
|
|
|
|
|
// The netvector_t is just here to carry the signed-ness, which we don't
|
|
|
|
|
// even need here. So act like the type is defined by the r-value
|
|
|
|
|
// length.
|
|
|
|
|
if (packed_dims.size() == 0) {
|
|
|
|
|
par_msv = length-1;
|
|
|
|
|
par_lsv = 0;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
ivl_assert(line, packed_dims.size() == 1);
|
|
|
|
|
|
|
|
|
|
netrange_t use_range = packed_dims[0];
|
|
|
|
|
par_msv = use_range.get_msb();
|
|
|
|
|
par_lsv = use_range.get_lsb();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|