1999-09-20 04:21:10 +02:00
|
|
|
/*
|
2010-01-10 04:57:01 +01:00
|
|
|
* Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com)
|
1999-09-20 04:21:10 +02:00
|
|
|
*
|
|
|
|
|
* This source code is free software; you can redistribute it
|
|
|
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
|
|
|
* General Public License as published by the Free Software
|
|
|
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
|
|
|
* any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
|
|
|
*/
|
|
|
|
|
|
2001-07-25 05:10:48 +02:00
|
|
|
# include "config.h"
|
2008-01-05 00:23:47 +01:00
|
|
|
# include <typeinfo>
|
|
|
|
|
# include <cstdlib>
|
|
|
|
|
# include <cstring>
|
2008-10-01 04:49:55 +02:00
|
|
|
# include <climits>
|
2003-03-11 00:40:53 +01:00
|
|
|
# include "compiler.h"
|
1999-09-20 04:21:10 +02:00
|
|
|
|
|
|
|
|
# include "pform.h"
|
|
|
|
|
# include "netlist.h"
|
2008-07-31 03:01:41 +02:00
|
|
|
# include "discipline.h"
|
2001-02-10 21:29:39 +01:00
|
|
|
# include "netmisc.h"
|
2001-06-23 21:53:03 +02:00
|
|
|
# include "util.h"
|
2007-03-02 02:55:36 +01:00
|
|
|
# include "ivl_assert.h"
|
1999-09-20 04:21:10 +02:00
|
|
|
|
2008-10-03 07:02:35 +02:00
|
|
|
bool type_is_vectorable(ivl_variable_type_t type)
|
2008-09-23 06:09:06 +02:00
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case IVL_VT_BOOL:
|
|
|
|
|
case IVL_VT_LOGIC:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-04 06:10:10 +01:00
|
|
|
static ivl_nature_t find_access_function(const pform_name_t&path)
|
2008-11-03 06:46:27 +01:00
|
|
|
{
|
|
|
|
|
if (path.size() != 1)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return access_function_nature[peek_tail_name(path)];
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-11 06:19:30 +01:00
|
|
|
/*
|
|
|
|
|
* Look at the signal to see if there is already a branch that
|
|
|
|
|
* connects the sig to the gnd. If there is, then return it. If not,
|
|
|
|
|
* return 0.
|
|
|
|
|
*/
|
|
|
|
|
static NetBranch* find_existing_implicit_branch(NetNet*sig, NetNet*gnd)
|
|
|
|
|
{
|
|
|
|
|
Nexus*nex = sig->pin(0).nexus();
|
|
|
|
|
for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) {
|
|
|
|
|
if (cur->is_equal(sig->pin(0)))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (cur->get_pin() != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
NetBranch*tmp = dynamic_cast<NetBranch*> (cur->get_obj());
|
|
|
|
|
if (tmp == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (tmp->name())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (tmp->pin(1).is_linked(gnd->pin(0)))
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-26 05:37:18 +02:00
|
|
|
NetExpr* elaborate_rval_expr(Design*des, NetScope*scope,
|
|
|
|
|
ivl_variable_type_t data_type_lv, int expr_wid_lv,
|
2008-10-11 05:42:07 +02:00
|
|
|
PExpr*expr)
|
2008-09-26 05:37:18 +02:00
|
|
|
{
|
2009-01-06 04:44:52 +01:00
|
|
|
bool unsized_flag = type_is_vectorable(data_type_lv)? false : true;
|
|
|
|
|
unsigned use_lval_wid = type_is_vectorable(data_type_lv)? expr_wid_lv : 0;
|
|
|
|
|
unsigned use_min_wid = expr_wid_lv;
|
2008-09-26 05:37:18 +02:00
|
|
|
|
2008-10-11 05:42:07 +02:00
|
|
|
/* Find out what the r-value width is going to be. We
|
|
|
|
|
guess it will be the l-value width, but it may turn
|
|
|
|
|
out to be something else based on self-determined
|
|
|
|
|
widths inside. */
|
2009-01-06 04:44:52 +01:00
|
|
|
ivl_variable_type_t rval_type = IVL_VT_NO_TYPE;
|
|
|
|
|
int expr_wid = expr->test_width(des, scope, use_min_wid, use_lval_wid, rval_type, unsized_flag);
|
2008-10-11 05:42:07 +02:00
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << expr->get_fileline() << ": debug: r-value tested "
|
|
|
|
|
<< "type=" << rval_type
|
|
|
|
|
<< ", width=" << expr_wid
|
2009-01-06 04:44:52 +01:00
|
|
|
<< ", min=" << use_min_wid
|
2008-10-11 05:42:07 +02:00
|
|
|
<< ", unsized_flag=" << (unsized_flag?"true":"false") << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-26 05:37:18 +02:00
|
|
|
switch (data_type_lv) {
|
|
|
|
|
case IVL_VT_REAL:
|
2008-09-26 06:22:21 +02:00
|
|
|
unsized_flag = true;
|
2008-09-26 05:37:18 +02:00
|
|
|
expr_wid = -2;
|
|
|
|
|
expr_wid_lv = -1;
|
|
|
|
|
break;
|
|
|
|
|
case IVL_VT_BOOL:
|
|
|
|
|
case IVL_VT_LOGIC:
|
|
|
|
|
break;
|
|
|
|
|
case IVL_VT_VOID:
|
|
|
|
|
case IVL_VT_NO_TYPE:
|
|
|
|
|
ivl_assert(*expr, 0);
|
|
|
|
|
expr_wid = -2;
|
|
|
|
|
expr_wid_lv = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr*result = elab_and_eval(des, scope, expr, expr_wid, expr_wid_lv);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2006-10-30 06:44:49 +01:00
|
|
|
/*
|
2008-01-29 21:19:59 +01:00
|
|
|
* The default behavior for the test_width method is to just return the
|
2006-10-30 06:44:49 +01:00
|
|
|
* minimum width that is passed in.
|
|
|
|
|
*/
|
2006-11-04 07:19:24 +01:00
|
|
|
unsigned PExpr::test_width(Design*des, NetScope*scope,
|
2008-09-27 07:20:11 +02:00
|
|
|
unsigned min, unsigned lval,
|
2008-10-11 05:42:07 +02:00
|
|
|
ivl_variable_type_t&, bool&)
|
2006-10-30 06:44:49 +01:00
|
|
|
{
|
|
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: test_width defaults to "
|
2007-10-02 05:38:20 +02:00
|
|
|
<< min << ", ignoring unsized_flag. typeid="
|
|
|
|
|
<< typeid(*this).name() << endl;
|
2006-10-30 06:44:49 +01:00
|
|
|
}
|
|
|
|
|
return min;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, int, bool) const
|
2000-03-08 05:36:53 +01:00
|
|
|
{
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: I do not know how to elaborate"
|
2000-12-10 23:01:35 +01:00
|
|
|
<< " expression. " << endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : Expression is: " << *this
|
2000-12-10 23:01:35 +01:00
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
2000-03-08 05:36:53 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-04 07:19:24 +01:00
|
|
|
unsigned PEBinary::test_width(Design*des, NetScope*scope,
|
2008-09-27 07:20:11 +02:00
|
|
|
unsigned min, unsigned lval,
|
2008-10-20 19:06:04 +02:00
|
|
|
ivl_variable_type_t&expr_type__,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&unsized_flag)
|
2006-10-30 06:44:49 +01:00
|
|
|
{
|
2008-09-27 07:20:11 +02:00
|
|
|
ivl_variable_type_t expr_type_left = IVL_VT_NO_TYPE;
|
|
|
|
|
ivl_variable_type_t expr_type_right= IVL_VT_NO_TYPE;
|
|
|
|
|
|
2008-10-14 07:06:03 +02:00
|
|
|
bool flag = unsized_flag;
|
|
|
|
|
|
|
|
|
|
bool flag_left = flag;
|
2008-09-27 07:20:11 +02:00
|
|
|
unsigned wid_left = left_->test_width(des,scope, min, 0, expr_type_left, flag_left);
|
2008-10-05 05:01:19 +02:00
|
|
|
|
2008-10-14 07:06:03 +02:00
|
|
|
bool flag_right = flag;
|
2008-09-27 07:20:11 +02:00
|
|
|
unsigned wid_right = right_->test_width(des,scope, min, 0, expr_type_right, flag_right);
|
2006-10-30 06:44:49 +01:00
|
|
|
|
2008-10-14 07:06:03 +02:00
|
|
|
if (flag_right && !flag) {
|
|
|
|
|
unsized_flag = true;
|
|
|
|
|
wid_left = left_->test_width(des, scope, min, 0, expr_type_left, unsized_flag);
|
2008-10-05 05:01:19 +02:00
|
|
|
}
|
|
|
|
|
|
2008-10-14 07:06:03 +02:00
|
|
|
if (flag_left && !flag) {
|
2006-10-30 06:44:49 +01:00
|
|
|
unsized_flag = true;
|
2008-10-14 07:06:03 +02:00
|
|
|
wid_right = right_->test_width(des, scope, min, 0, expr_type_right, unsized_flag);
|
|
|
|
|
}
|
2006-10-30 06:44:49 +01:00
|
|
|
|
2008-09-27 07:20:11 +02:00
|
|
|
if (expr_type_left == IVL_VT_REAL || expr_type_right == IVL_VT_REAL)
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = IVL_VT_REAL;
|
2008-09-27 07:20:11 +02:00
|
|
|
else if (expr_type_left==IVL_VT_LOGIC || expr_type_right==IVL_VT_LOGIC)
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = IVL_VT_LOGIC;
|
2008-09-27 07:20:11 +02:00
|
|
|
else
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = IVL_VT_BOOL;
|
2008-09-27 07:20:11 +02:00
|
|
|
|
2006-10-30 06:44:49 +01:00
|
|
|
switch (op_) {
|
|
|
|
|
case '+':
|
|
|
|
|
case '-':
|
|
|
|
|
if (wid_left > min)
|
|
|
|
|
min = wid_left;
|
|
|
|
|
if (wid_right > min)
|
|
|
|
|
min = wid_right;
|
2008-10-14 07:06:03 +02:00
|
|
|
if (unsized_flag && type_is_vectorable(expr_type_))
|
|
|
|
|
min += 1;
|
2006-10-30 06:44:49 +01:00
|
|
|
break;
|
|
|
|
|
|
2008-10-06 01:44:17 +02:00
|
|
|
case '*':
|
2008-10-20 19:06:04 +02:00
|
|
|
if (unsized_flag && type_is_vectorable(expr_type_)) {
|
2008-10-06 01:44:17 +02:00
|
|
|
unsigned use_wid = wid_left + wid_right;
|
|
|
|
|
if (use_wid > integer_width)
|
|
|
|
|
use_wid = integer_width;
|
|
|
|
|
if (use_wid > min)
|
|
|
|
|
min = use_wid;
|
|
|
|
|
}
|
|
|
|
|
if (wid_left > min)
|
|
|
|
|
min = wid_left;
|
|
|
|
|
if (wid_right > min)
|
|
|
|
|
min = wid_right;
|
|
|
|
|
break;
|
|
|
|
|
|
2008-09-23 06:09:06 +02:00
|
|
|
case 'l': // << Should be handled by PEBShift
|
|
|
|
|
case '<': // < Should be handled by PEBComp
|
|
|
|
|
case '>': // > Should be handled by PEBComp
|
|
|
|
|
case 'e': // == Should be handled by PEBComp
|
|
|
|
|
case 'E': // === Should be handled by PEBComp
|
|
|
|
|
case 'L': // <= Should be handled by PEBComp
|
|
|
|
|
case 'G': // >= Should be handled by PEBComp
|
|
|
|
|
case 'n': // != Should be handled by PEBComp
|
|
|
|
|
case 'N': // !== Should be handled by PEBComp
|
2008-11-28 23:40:25 +01:00
|
|
|
case 'p': // ** should be handled by PEBPower
|
2008-09-23 06:09:06 +02:00
|
|
|
ivl_assert(*this, 0);
|
2006-10-30 06:44:49 +01:00
|
|
|
default:
|
|
|
|
|
if (wid_left > min)
|
|
|
|
|
min = wid_left;
|
|
|
|
|
if (wid_right > min)
|
|
|
|
|
min = wid_right;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-11 05:42:07 +02:00
|
|
|
if (type_is_vectorable(expr_type_))
|
|
|
|
|
expr_width_ = min;
|
|
|
|
|
else
|
|
|
|
|
expr_width_ = 1;
|
|
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-10-11 05:42:07 +02:00
|
|
|
return expr_width_;
|
2006-10-30 06:44:49 +01:00
|
|
|
}
|
|
|
|
|
|
1999-09-30 00:57:10 +02:00
|
|
|
/*
|
|
|
|
|
* Elaborate binary expressions. This involves elaborating the left
|
|
|
|
|
* and right sides, and creating one of a variety of different NetExpr
|
2004-10-04 03:10:51 +02:00
|
|
|
* types.
|
1999-09-30 00:57:10 +02:00
|
|
|
*/
|
2008-04-22 20:23:24 +02:00
|
|
|
NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope,
|
2006-06-02 06:48:49 +02:00
|
|
|
int expr_wid, bool) const
|
1999-09-30 00:57:10 +02:00
|
|
|
{
|
2001-11-19 02:54:14 +01:00
|
|
|
assert(left_);
|
|
|
|
|
assert(right_);
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, false);
|
|
|
|
|
NetExpr*rp = right_->elaborate_expr(des, scope, expr_wid, false);
|
1999-09-30 00:57:10 +02:00
|
|
|
if ((lp == 0) || (rp == 0)) {
|
|
|
|
|
delete lp;
|
|
|
|
|
delete rp;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-23 06:09:06 +02:00
|
|
|
// Handle the special case that one of the operands is a real
|
|
|
|
|
// value and the other is a vector type. In that case,
|
|
|
|
|
// re-elaborate the vectorable argument as self-determined
|
|
|
|
|
// lossless.
|
|
|
|
|
if (lp->expr_type()==IVL_VT_REAL
|
|
|
|
|
&& type_is_vectorable(rp->expr_type())
|
|
|
|
|
&& expr_wid != -2) {
|
|
|
|
|
delete rp;
|
|
|
|
|
rp = right_->elaborate_expr(des, scope, -2, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rp->expr_type()==IVL_VT_REAL
|
|
|
|
|
&& type_is_vectorable(lp->expr_type())
|
|
|
|
|
&& expr_wid != -2) {
|
|
|
|
|
delete lp;
|
|
|
|
|
lp = left_->elaborate_expr(des, scope, -2, false);
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-22 20:23:24 +02:00
|
|
|
NetExpr*tmp = elaborate_eval_expr_base_(des, lp, rp, expr_wid);
|
2006-10-30 06:44:49 +01:00
|
|
|
return tmp;
|
|
|
|
|
}
|
2001-11-19 03:54:12 +01:00
|
|
|
|
2008-12-19 06:33:31 +01:00
|
|
|
void suppress_binary_operand_sign_if_needed(NetExpr*lp, NetExpr*rp)
|
2007-11-25 05:32:35 +01:00
|
|
|
{
|
2008-10-11 18:20:49 +02:00
|
|
|
// If an argument is a non-vector type, then this type
|
|
|
|
|
// suppression does not apply.
|
|
|
|
|
if (! type_is_vectorable(lp->expr_type()))
|
|
|
|
|
return;
|
|
|
|
|
if (! type_is_vectorable(rp->expr_type()))
|
|
|
|
|
return;
|
|
|
|
|
|
2007-11-25 05:32:35 +01:00
|
|
|
// If either operand is unsigned, then treat the whole
|
2007-12-18 02:42:09 +01:00
|
|
|
// expression as unsigned. This test needs to be done here
|
2007-11-25 05:32:35 +01:00
|
|
|
// instead of in *_expr_base_ because it needs to be done
|
|
|
|
|
// ahead of any subexpression evaluation (because they need to
|
|
|
|
|
// know their signedness to evaluate) and because there are
|
|
|
|
|
// exceptions to this rule.
|
|
|
|
|
if (! lp->has_sign())
|
|
|
|
|
rp->cast_signed(false);
|
|
|
|
|
if (! rp->has_sign())
|
|
|
|
|
lp->cast_signed(false);
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-22 20:23:24 +02:00
|
|
|
NetExpr* PEBinary::elaborate_eval_expr_base_(Design*des,
|
2006-10-30 06:44:49 +01:00
|
|
|
NetExpr*lp,
|
2006-11-04 07:19:24 +01:00
|
|
|
NetExpr*rp,
|
|
|
|
|
int expr_wid) const
|
2006-10-30 06:44:49 +01:00
|
|
|
{
|
1999-09-30 00:57:10 +02:00
|
|
|
/* If either expression can be evaluated ahead of time, then
|
|
|
|
|
do so. This can prove helpful later. */
|
2008-03-08 03:51:50 +01:00
|
|
|
eval_expr(lp);
|
|
|
|
|
eval_expr(rp);
|
1999-09-30 00:57:10 +02:00
|
|
|
|
2006-11-04 07:19:24 +01:00
|
|
|
return elaborate_expr_base_(des, lp, rp, expr_wid);
|
2000-03-12 19:22:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This is common elaboration of the operator. It presumes that the
|
|
|
|
|
* operands are elaborated as necessary, and all I need to do is make
|
|
|
|
|
* the correct NetEBinary object and connect the parameters.
|
|
|
|
|
*/
|
2008-04-22 20:23:24 +02:00
|
|
|
NetExpr* PEBinary::elaborate_expr_base_(Design*des,
|
2008-08-21 06:47:07 +02:00
|
|
|
NetExpr*lp, NetExpr*rp,
|
2008-12-19 06:33:31 +01:00
|
|
|
int expr_wid, bool is_pexpr) const
|
2000-03-12 19:22:11 +01:00
|
|
|
{
|
2007-10-09 04:58:49 +02:00
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: elaborate expression "
|
2007-10-09 04:58:49 +02:00
|
|
|
<< *this << " expr_wid=" << expr_wid << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-22 20:23:24 +02:00
|
|
|
NetExpr*tmp;
|
2000-03-12 19:22:11 +01:00
|
|
|
|
1999-09-30 00:57:10 +02:00
|
|
|
switch (op_) {
|
|
|
|
|
default:
|
|
|
|
|
tmp = new NetEBinary(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'a':
|
|
|
|
|
case 'o':
|
2008-10-30 04:31:26 +01:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "Elaboration of " << human_readable_op(op_)
|
|
|
|
|
<< " Should have been handled in NetEBLogic::elaborate."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
1999-09-30 00:57:10 +02:00
|
|
|
|
2006-07-31 05:50:17 +02:00
|
|
|
case 'p':
|
|
|
|
|
tmp = new NetEBPow(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
break;
|
|
|
|
|
|
2000-01-13 04:35:35 +01:00
|
|
|
case '*':
|
2008-12-19 06:33:31 +01:00
|
|
|
tmp = elaborate_expr_base_mult_(des, lp, rp, expr_wid, is_pexpr);
|
2000-01-13 04:35:35 +01:00
|
|
|
break;
|
|
|
|
|
|
2000-04-28 20:43:23 +02:00
|
|
|
case '%':
|
2006-08-09 07:19:08 +02:00
|
|
|
case '/':
|
2008-12-19 06:33:31 +01:00
|
|
|
tmp = elaborate_expr_base_div_(des, lp, rp, expr_wid, is_pexpr);
|
2000-04-28 20:43:23 +02:00
|
|
|
break;
|
|
|
|
|
|
2008-11-28 23:40:25 +01:00
|
|
|
case 'l':
|
|
|
|
|
case 'r':
|
|
|
|
|
case 'R':
|
|
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "Elaboration of " << human_readable_op(op_)
|
|
|
|
|
<< " Should have been handled in NetEBShift::elaborate."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
1999-09-30 00:57:10 +02:00
|
|
|
|
|
|
|
|
case '^':
|
|
|
|
|
case '&':
|
|
|
|
|
case '|':
|
2002-09-18 06:08:45 +02:00
|
|
|
case 'O': // NOR (~|)
|
2002-09-12 17:49:43 +02:00
|
|
|
case 'A': // NAND (~&)
|
1999-09-30 04:43:01 +02:00
|
|
|
case 'X':
|
2008-10-01 06:35:09 +02:00
|
|
|
tmp = elaborate_expr_base_bits_(des, lp, rp, expr_wid);
|
1999-09-30 00:57:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '+':
|
|
|
|
|
case '-':
|
2008-12-19 06:33:31 +01:00
|
|
|
tmp = elaborate_expr_base_add_(des, lp, rp, expr_wid, is_pexpr);
|
1999-09-30 00:57:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'E': /* === */
|
|
|
|
|
case 'N': /* !== */
|
2005-09-01 06:10:47 +02:00
|
|
|
case 'e': /* == */
|
|
|
|
|
case 'n': /* != */
|
1999-09-30 00:57:10 +02:00
|
|
|
case 'L': /* <= */
|
|
|
|
|
case 'G': /* >= */
|
|
|
|
|
case '<':
|
|
|
|
|
case '>':
|
2008-10-30 03:34:44 +01:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "Elaboration of " << human_readable_op(op_)
|
|
|
|
|
<< " Should have been handled in NetEBComp::elaborate."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
2008-05-04 06:54:42 +02:00
|
|
|
|
|
|
|
|
case 'm': // min(l,r)
|
|
|
|
|
case 'M': // max(l,r)
|
|
|
|
|
tmp = new NetEBMinMax(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
break;
|
1999-09-30 00:57:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-01 06:35:09 +02:00
|
|
|
NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des,
|
|
|
|
|
NetExpr*lp, NetExpr*rp,
|
|
|
|
|
int expr_wid) const
|
|
|
|
|
{
|
2009-02-14 03:25:54 +01:00
|
|
|
if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< human_readable_op(op_)
|
|
|
|
|
<< " operator may not have REAL operands." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-02 07:38:53 +02:00
|
|
|
// If either of the arguments is unsigned, then process both
|
|
|
|
|
// of them as unsigned. This only impacts the padding that is
|
|
|
|
|
// done to get the operands to the expr_wid.
|
2009-04-07 02:11:14 +02:00
|
|
|
if (! lp->has_sign()) rp->cast_signed(false);
|
|
|
|
|
if (! rp->has_sign()) lp->cast_signed(false);
|
2008-10-02 07:38:53 +02:00
|
|
|
|
2008-10-01 06:35:09 +02:00
|
|
|
if (expr_wid > 0) {
|
|
|
|
|
if (type_is_vectorable(lp->expr_type()))
|
2008-11-19 02:17:19 +01:00
|
|
|
lp = pad_to_width(lp, expr_wid, *this);
|
2008-10-01 06:35:09 +02:00
|
|
|
if (type_is_vectorable(rp->expr_type()))
|
2008-11-19 02:17:19 +01:00
|
|
|
rp = pad_to_width(rp, expr_wid, *this);
|
2008-10-01 06:35:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetEBBits*tmp = new NetEBBits(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-26 05:37:18 +02:00
|
|
|
NetExpr* PEBinary::elaborate_expr_base_div_(Design*des,
|
|
|
|
|
NetExpr*lp, NetExpr*rp,
|
2008-12-19 06:33:31 +01:00
|
|
|
int expr_wid, bool is_pexpr) const
|
2008-09-26 05:37:18 +02:00
|
|
|
{
|
|
|
|
|
/* The % operator does not support real arguments in
|
|
|
|
|
baseline Verilog. But we allow it in our extended
|
|
|
|
|
form of Verilog. */
|
|
|
|
|
if (op_ == '%' && ! gn_icarus_misc_flag) {
|
2009-02-14 03:25:54 +01:00
|
|
|
if (lp->expr_type() == IVL_VT_REAL ||
|
|
|
|
|
rp->expr_type() == IVL_VT_REAL) {
|
2008-09-26 05:37:18 +02:00
|
|
|
cerr << get_fileline() << ": error: Modulus operator "
|
|
|
|
|
"may not have REAL operands." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-07 02:11:14 +02:00
|
|
|
// If either of the arguments is unsigned, then process both
|
|
|
|
|
// of them as unsigned. This only impacts the padding that is
|
|
|
|
|
// done to get the operands to the expr_wid.
|
|
|
|
|
if (! lp->has_sign()) rp->cast_signed(false);
|
|
|
|
|
if (! rp->has_sign()) lp->cast_signed(false);
|
|
|
|
|
|
2008-10-31 05:19:56 +01:00
|
|
|
/* The original elaboration of the left and right expressions
|
|
|
|
|
already tried to elaborate to the expr_wid. If the
|
|
|
|
|
expressions are not that width by now, then they need to be
|
|
|
|
|
padded. The divide expression operands must be the width
|
|
|
|
|
of the output. */
|
|
|
|
|
if (expr_wid > 0) {
|
2008-11-19 02:17:19 +01:00
|
|
|
lp = pad_to_width(lp, expr_wid, *this);
|
|
|
|
|
rp = pad_to_width(rp, expr_wid, *this);
|
2008-10-31 05:19:56 +01:00
|
|
|
}
|
|
|
|
|
|
2008-09-26 05:37:18 +02:00
|
|
|
NetEBDiv*tmp = new NetEBDiv(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-21 06:47:07 +02:00
|
|
|
NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des,
|
|
|
|
|
NetExpr*lp, NetExpr*rp,
|
|
|
|
|
int expr_wid) const
|
|
|
|
|
{
|
2009-02-14 03:25:54 +01:00
|
|
|
if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< human_readable_op(op_)
|
|
|
|
|
<< " operator may not have REAL operands." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-21 06:47:07 +02:00
|
|
|
NetExpr*tmp;
|
|
|
|
|
|
2008-08-22 03:58:38 +02:00
|
|
|
long use_wid = lp->expr_width();
|
|
|
|
|
if (expr_wid > 0)
|
|
|
|
|
use_wid = expr_wid;
|
|
|
|
|
|
|
|
|
|
if (use_wid == 0) {
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Oops, left expression width is not known, "
|
|
|
|
|
<< "so expression width is not known. Punt." << endl;
|
|
|
|
|
tmp = new NetEBShift(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-27 06:33:24 +02:00
|
|
|
// If the left expression is constant, then there are some
|
|
|
|
|
// special cases we can work with. If the left expression is
|
|
|
|
|
// not constant, but the right expression is constant, then
|
|
|
|
|
// there are some other interesting cases. But if neither are
|
|
|
|
|
// constant, then there is the general case.
|
|
|
|
|
|
2008-08-21 06:47:07 +02:00
|
|
|
if (NetEConst*lpc = dynamic_cast<NetEConst*> (lp)) {
|
2009-02-19 18:22:56 +01:00
|
|
|
|
|
|
|
|
// Special case: The left expression is zero. No matter
|
|
|
|
|
// what the shift, the result is going to be zero.
|
|
|
|
|
if (lpc->value().is_defined() && lpc->value().is_zero()) {
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Shift of zero always returns zero."
|
|
|
|
|
<< " Elaborate as constant zero." << endl;
|
|
|
|
|
|
|
|
|
|
tmp = make_const_0(use_wid);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-21 06:47:07 +02:00
|
|
|
if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
|
|
|
|
|
// Handle the super-special case that both
|
|
|
|
|
// operands are constants. Precalculate the
|
|
|
|
|
// entire value here.
|
|
|
|
|
verinum lpval = lpc->value();
|
|
|
|
|
unsigned shift = rpc->value().as_ulong();
|
|
|
|
|
verinum result = lpc->value() << shift;
|
|
|
|
|
// If the l-value has explicit size, or
|
|
|
|
|
// there is a context determined size, use that.
|
|
|
|
|
if (lpval.has_len() || expr_wid > 0) {
|
|
|
|
|
int use_len = lpval.len();
|
2008-08-27 06:33:24 +02:00
|
|
|
if (expr_wid > 0 && expr_wid > use_len)
|
2008-08-21 06:47:07 +02:00
|
|
|
use_len = expr_wid;
|
2008-08-27 06:33:24 +02:00
|
|
|
result = verinum(result, use_len);
|
2008-08-21 06:47:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp = new NetEConst(result);
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
2008-08-27 06:33:24 +02:00
|
|
|
<< "Precalculate " << *lpc << " << " << shift
|
|
|
|
|
<< " to constant " << *tmp
|
|
|
|
|
<< " (expr_wid=" << expr_wid << ")" << endl;
|
2008-08-21 06:47:07 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// Handle the special case that the left
|
|
|
|
|
// operand is constant. If it is unsized, we
|
|
|
|
|
// may have to expand it to an integer width.
|
|
|
|
|
verinum lpval = lpc->value();
|
|
|
|
|
if (lpval.len() < integer_width && !lpval.has_len()) {
|
|
|
|
|
lpval = verinum(lpval, integer_width);
|
|
|
|
|
lpc = new NetEConst(lpval);
|
|
|
|
|
lpc->set_line(*lp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp = new NetEBShift(op_, lpc, rp);
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Adjust " << *this
|
|
|
|
|
<< " to this " << *tmp
|
|
|
|
|
<< " to allow for integer widths." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
|
|
|
|
|
long shift = rpc->value().as_long();
|
2008-10-20 19:06:04 +02:00
|
|
|
use_wid = lp->expr_width();
|
2008-08-21 06:47:07 +02:00
|
|
|
if (expr_wid > 0)
|
|
|
|
|
use_wid = expr_wid;
|
|
|
|
|
|
|
|
|
|
if (shift >= use_wid || (-shift) >= (long)lp->expr_width()) {
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Value left-shifted " << shift
|
|
|
|
|
<< " beyond width of " << use_wid
|
|
|
|
|
<< ". Elaborate as constant zero." << endl;
|
|
|
|
|
|
|
|
|
|
tmp = make_const_0(use_wid);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Left shift expression by constant "
|
|
|
|
|
<< shift << " bits. (use_wid=" << use_wid << ")" << endl;
|
2008-11-19 02:17:19 +01:00
|
|
|
lp = pad_to_width(lp, use_wid, *this);
|
2008-08-21 06:47:07 +02:00
|
|
|
tmp = new NetEBShift(op_, lp, rp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// Left side is not constant, so handle it the
|
|
|
|
|
// default way.
|
|
|
|
|
if (expr_wid >= 0)
|
2008-11-19 02:17:19 +01:00
|
|
|
lp = pad_to_width(lp, expr_wid, *this);
|
2008-08-21 06:47:07 +02:00
|
|
|
tmp = new NetEBShift(op_, lp, rp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des,
|
|
|
|
|
NetExpr*lp, NetExpr*rp,
|
|
|
|
|
int expr_wid) const
|
|
|
|
|
{
|
2009-02-14 03:25:54 +01:00
|
|
|
if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< human_readable_op(op_)
|
|
|
|
|
<< " operator may not have REAL operands." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-21 06:47:07 +02:00
|
|
|
NetExpr*tmp;
|
|
|
|
|
|
|
|
|
|
long use_wid = lp->expr_width();
|
|
|
|
|
if (expr_wid > 0)
|
|
|
|
|
use_wid = expr_wid;
|
|
|
|
|
|
|
|
|
|
if (use_wid == 0) {
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Oops, left expression width is not known, "
|
|
|
|
|
<< "so expression width is not known. Punt." << endl;
|
|
|
|
|
tmp = new NetEBShift(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-19 18:22:56 +01:00
|
|
|
if (NetEConst*lpc = dynamic_cast<NetEConst*> (lp)) {
|
|
|
|
|
|
|
|
|
|
// Special case: The left expression is zero. No matter
|
|
|
|
|
// what the shift, the result is going to be zero.
|
|
|
|
|
if (lpc->value().is_defined() && lpc->value().is_zero()) {
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Shift of zero always returns zero."
|
|
|
|
|
<< " Elaborate as constant zero." << endl;
|
|
|
|
|
|
|
|
|
|
tmp = make_const_0(use_wid);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-21 06:47:07 +02:00
|
|
|
if (NetEConst*rpc = dynamic_cast<NetEConst*> (rp)) {
|
2009-03-31 02:14:36 +02:00
|
|
|
unsigned long shift = rpc->value().as_ulong();
|
2008-08-21 06:47:07 +02:00
|
|
|
|
2009-01-13 04:19:00 +01:00
|
|
|
// Special case: The shift is the size of the entire
|
|
|
|
|
// left operand, and the shift is unsigned. Elaborate as
|
|
|
|
|
// a constant-0.
|
2009-03-31 02:14:36 +02:00
|
|
|
if ((op_=='r' || (lp->has_sign()==false)) &&
|
|
|
|
|
shift >= lp->expr_width()) {
|
2009-01-13 04:19:00 +01:00
|
|
|
|
2008-08-21 06:47:07 +02:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Value right-shifted " << shift
|
|
|
|
|
<< " beyond width of " << lp->expr_width()
|
|
|
|
|
<< ". Elaborate as constant zero." << endl;
|
|
|
|
|
|
|
|
|
|
tmp = make_const_0(use_wid);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-13 04:19:00 +01:00
|
|
|
// Special case: the shift is the size of the entire
|
|
|
|
|
// left operand, and the shift is signed. Elaborate as a
|
|
|
|
|
// replication of the top bit of the left expression.
|
2009-03-31 02:14:36 +02:00
|
|
|
//
|
|
|
|
|
// The above test assures us that op_ == 'R' && the left
|
|
|
|
|
// argument is signed when the shift is greater than the
|
|
|
|
|
// expression width.
|
|
|
|
|
if (shift >= lp->expr_width()) {
|
2009-01-13 04:19:00 +01:00
|
|
|
|
2008-08-21 06:47:07 +02:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Value signed-right-shifted " << shift
|
|
|
|
|
<< " beyond width of " << lp->expr_width()
|
|
|
|
|
<< ". Elaborate as replicated top bit." << endl;
|
|
|
|
|
|
2008-09-28 17:57:25 +02:00
|
|
|
ivl_assert(*this, lp->expr_width() > 0);
|
|
|
|
|
ivl_assert(*this, use_wid > 0);
|
|
|
|
|
|
2008-08-21 06:47:07 +02:00
|
|
|
tmp = new NetEConst(verinum(lp->expr_width()-1));
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp = new NetESelect(lp, tmp, 1);
|
|
|
|
|
tmp->cast_signed(true);
|
2008-11-19 02:17:19 +01:00
|
|
|
tmp = pad_to_width(tmp, use_wid, *this);
|
2008-08-21 06:47:07 +02:00
|
|
|
return tmp;
|
2009-01-13 04:19:00 +01:00
|
|
|
}
|
2008-08-21 06:47:07 +02:00
|
|
|
|
2009-01-13 04:19:00 +01:00
|
|
|
// If this is lossless, then pad the left expression
|
|
|
|
|
// enough to cover the right shift.
|
2009-03-31 02:14:36 +02:00
|
|
|
if (expr_wid == -2 && use_wid+shift > lp->expr_width()) {
|
|
|
|
|
lp->cast_signed(lp->has_sign() && op_=='R');
|
2009-01-13 04:19:00 +01:00
|
|
|
lp = pad_to_width(lp, use_wid + shift, *this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp = new NetEConst(verinum(shift));
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
long tmp_wid = lp->expr_width() - shift;
|
|
|
|
|
if (tmp_wid > use_wid)
|
|
|
|
|
tmp_wid = use_wid;
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, tmp_wid > 0);
|
|
|
|
|
ivl_assert(*this, use_wid > 0);
|
|
|
|
|
|
|
|
|
|
// Implement the right-shift by part-selecting the low
|
|
|
|
|
// bits out. Pad the result of the part select back out
|
|
|
|
|
// to the desired size.
|
|
|
|
|
tmp = new NetESelect(lp, tmp, tmp_wid);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->cast_signed(lp->has_sign() && op_=='R');
|
|
|
|
|
tmp = pad_to_width(tmp, use_wid, *this);
|
|
|
|
|
return tmp;
|
2008-08-21 06:47:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Falback, handle the general case.
|
|
|
|
|
if (expr_wid > 0)
|
2008-11-19 02:17:19 +01:00
|
|
|
lp = pad_to_width(lp, expr_wid, *this);
|
2008-08-21 06:47:07 +02:00
|
|
|
tmp = new NetEBShift(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-20 07:00:29 +02:00
|
|
|
NetExpr* PEBinary::elaborate_expr_base_mult_(Design*des,
|
|
|
|
|
NetExpr*lp, NetExpr*rp,
|
2008-12-19 06:33:31 +01:00
|
|
|
int expr_wid, bool is_pexpr) const
|
2008-09-20 07:00:29 +02:00
|
|
|
{
|
|
|
|
|
// First, Make sure that signed arguments are padded to the
|
|
|
|
|
// width of the output. This is necessary for 2s complement
|
|
|
|
|
// multiplication to come out right.
|
|
|
|
|
if (expr_wid > 0) {
|
|
|
|
|
if (lp->has_sign() && lp->expr_type() != IVL_VT_REAL)
|
2008-11-19 02:17:19 +01:00
|
|
|
lp = pad_to_width(lp, expr_wid, *this);
|
2008-09-20 07:00:29 +02:00
|
|
|
if (rp->has_sign() && rp->expr_type() != IVL_VT_REAL)
|
2008-11-19 02:17:19 +01:00
|
|
|
rp = pad_to_width(rp, expr_wid, *this);
|
2008-09-20 07:00:29 +02:00
|
|
|
}
|
|
|
|
|
|
2008-10-05 05:01:19 +02:00
|
|
|
// Keep constants on the right side.
|
|
|
|
|
if (dynamic_cast<NetEConst*>(lp)) {
|
|
|
|
|
NetExpr*tmp = lp;
|
|
|
|
|
lp = rp;
|
|
|
|
|
rp = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle a few special case multiplies against constants.
|
|
|
|
|
if (NetEConst*rp_const = dynamic_cast<NetEConst*> (rp)) {
|
|
|
|
|
verinum rp_val = rp_const->value();
|
|
|
|
|
|
|
|
|
|
int use_wid = expr_wid;
|
|
|
|
|
if (use_wid < 0)
|
|
|
|
|
use_wid = max(rp->expr_width(), lp->expr_width());
|
|
|
|
|
|
|
|
|
|
if (! rp_val.is_defined()) {
|
|
|
|
|
NetEConst*tmp = make_const_x(use_wid);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rp_val.is_zero()) {
|
|
|
|
|
NetEConst*tmp = make_const_0(use_wid);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-11 17:39:06 +02:00
|
|
|
// If this expression is unsigned, then make sure the
|
|
|
|
|
// arguments are unsigned so that the padding below doesn't
|
|
|
|
|
// cause any sign extension to happen.
|
2008-12-19 06:33:31 +01:00
|
|
|
if (! is_pexpr)
|
|
|
|
|
suppress_binary_operand_sign_if_needed(lp, rp);
|
2008-10-11 17:39:06 +02:00
|
|
|
|
|
|
|
|
|
2008-09-20 07:00:29 +02:00
|
|
|
// Multiply will guess a width that is the sum of the
|
|
|
|
|
// widths of the operand. If that sum is too small, then
|
|
|
|
|
// pad one of the arguments enough that the sum is the
|
|
|
|
|
// desired width.
|
|
|
|
|
if (expr_wid > (long)(lp->expr_width() + rp->expr_width()))
|
2008-11-19 02:17:19 +01:00
|
|
|
lp = pad_to_width(lp, expr_wid - rp->expr_width(), *this);
|
2008-09-20 07:00:29 +02:00
|
|
|
|
|
|
|
|
NetEBMult*tmp = new NetEBMult(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
|
|
|
|
|
if (expr_wid > 0)
|
|
|
|
|
tmp->set_width(expr_wid, false);
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-27 06:33:24 +02:00
|
|
|
NetExpr* PEBinary::elaborate_expr_base_add_(Design*des,
|
|
|
|
|
NetExpr*lp, NetExpr*rp,
|
2008-12-19 06:33:31 +01:00
|
|
|
int expr_wid, bool is_pexpr) const
|
2008-08-27 06:33:24 +02:00
|
|
|
{
|
|
|
|
|
NetExpr*tmp;
|
2008-09-23 06:09:06 +02:00
|
|
|
bool use_lossless_flag = expr_wid == -2;
|
|
|
|
|
|
|
|
|
|
// If this expression is not vectorable, then do NOT pass the
|
|
|
|
|
// lossless flag to the NetEBAdd constructor. For non-
|
|
|
|
|
// vectorable, lossless is implicit.
|
|
|
|
|
if (! type_is_vectorable(lp->expr_type()))
|
|
|
|
|
use_lossless_flag = false;
|
|
|
|
|
if (! type_is_vectorable(rp->expr_type()))
|
|
|
|
|
use_lossless_flag = false;
|
|
|
|
|
|
2008-10-11 17:39:06 +02:00
|
|
|
// If the expression is unsigned, then force the operands to
|
|
|
|
|
// unsigned so taht the set_width below doesn't cause them to
|
|
|
|
|
// be sign-extended.
|
2008-12-19 06:33:31 +01:00
|
|
|
if (! is_pexpr)
|
|
|
|
|
suppress_binary_operand_sign_if_needed(lp, rp);
|
2008-10-11 17:39:06 +02:00
|
|
|
|
2008-09-23 06:09:06 +02:00
|
|
|
tmp = new NetEBAdd(op_, lp, rp, use_lossless_flag);
|
2008-09-30 06:02:37 +02:00
|
|
|
if (expr_wid > 0 && type_is_vectorable(tmp->expr_type()))
|
2008-08-27 06:33:24 +02:00
|
|
|
tmp->set_width(expr_wid);
|
2008-09-30 06:02:37 +02:00
|
|
|
|
2008-08-27 06:33:24 +02:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-27 07:20:11 +02:00
|
|
|
unsigned PEBComp::test_width(Design*, NetScope*,unsigned, unsigned,
|
2008-10-20 19:06:04 +02:00
|
|
|
ivl_variable_type_t&expr_type__,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&)
|
2006-10-30 06:44:49 +01:00
|
|
|
{
|
2008-10-19 07:00:22 +02:00
|
|
|
expr_type_ = IVL_VT_LOGIC;
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-10-19 07:00:22 +02:00
|
|
|
expr_width_ = 1;
|
2006-10-30 06:44:49 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-22 20:23:24 +02:00
|
|
|
NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope,
|
2008-10-20 19:06:04 +02:00
|
|
|
int expr_width_dummy, bool sys_task_arg) const
|
2006-10-30 06:44:49 +01:00
|
|
|
{
|
|
|
|
|
assert(left_);
|
|
|
|
|
assert(right_);
|
|
|
|
|
|
2006-11-04 07:19:24 +01:00
|
|
|
bool unsized_flag = false;
|
2008-09-27 07:20:11 +02:00
|
|
|
ivl_variable_type_t left_type = IVL_VT_NO_TYPE;
|
|
|
|
|
unsigned left_width = left_->test_width(des, scope, 0, 0, left_type, unsized_flag);
|
2006-11-04 07:19:24 +01:00
|
|
|
bool save_flag = unsized_flag;
|
2008-09-27 07:20:11 +02:00
|
|
|
ivl_variable_type_t right_type = IVL_VT_NO_TYPE;
|
|
|
|
|
unsigned right_width = right_->test_width(des, scope, 0, 0, right_type, unsized_flag);
|
2006-11-04 07:19:24 +01:00
|
|
|
|
|
|
|
|
if (save_flag != unsized_flag)
|
2008-09-27 07:20:11 +02:00
|
|
|
left_width = left_->test_width(des, scope, 0, 0, left_type, unsized_flag);
|
2006-11-04 07:19:24 +01:00
|
|
|
|
2006-10-30 06:44:49 +01:00
|
|
|
/* Width of operands is self-determined. */
|
2008-11-16 06:42:02 +01:00
|
|
|
|
|
|
|
|
int use_wid_l = left_width;
|
|
|
|
|
if (type_is_vectorable(left_type) && (right_width > left_width))
|
|
|
|
|
use_wid_l = right_width;
|
|
|
|
|
|
|
|
|
|
int use_wid_r = right_width;
|
|
|
|
|
if (type_is_vectorable(right_type) && (left_width > right_width))
|
|
|
|
|
use_wid_r = left_width;
|
2006-11-04 07:19:24 +01:00
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: "
|
2006-11-04 07:19:24 +01:00
|
|
|
<< "Comparison expression operands are "
|
|
|
|
|
<< left_width << " bits and "
|
|
|
|
|
<< right_width << " bits. Resorting to "
|
2008-11-16 06:42:02 +01:00
|
|
|
<< use_wid_l << " bits and "
|
|
|
|
|
<< use_wid_r << " bits." << endl;
|
2006-11-04 07:19:24 +01:00
|
|
|
}
|
2006-10-30 06:44:49 +01:00
|
|
|
|
2008-11-16 06:42:02 +01:00
|
|
|
NetExpr*lp = left_->elaborate_expr(des, scope, use_wid_l, false);
|
|
|
|
|
NetExpr*rp = right_->elaborate_expr(des, scope, use_wid_r, false);
|
2006-10-30 06:44:49 +01:00
|
|
|
if ((lp == 0) || (rp == 0)) {
|
|
|
|
|
delete lp;
|
|
|
|
|
delete rp;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-19 06:33:31 +01:00
|
|
|
suppress_binary_operand_sign_if_needed(lp, rp);
|
2007-11-25 05:32:35 +01:00
|
|
|
|
2008-10-11 18:20:49 +02:00
|
|
|
// The arguments of a compare need to have matching widths, so
|
|
|
|
|
// pad the width here. This matters because if the arguments
|
|
|
|
|
// are signed, then this padding will do sign extension.
|
|
|
|
|
if (type_is_vectorable(lp->expr_type()))
|
2008-11-19 02:17:19 +01:00
|
|
|
lp = pad_to_width(lp, use_wid_l, *this);
|
2008-10-11 18:20:49 +02:00
|
|
|
if (type_is_vectorable(rp->expr_type()))
|
2008-11-19 02:17:19 +01:00
|
|
|
rp = pad_to_width(rp, use_wid_r, *this);
|
2007-11-25 05:32:35 +01:00
|
|
|
|
2008-11-16 06:42:02 +01:00
|
|
|
eval_expr(lp, use_wid_l);
|
|
|
|
|
eval_expr(rp, use_wid_r);
|
2008-10-30 03:34:44 +01:00
|
|
|
|
|
|
|
|
// Handle some operand-specific special cases...
|
|
|
|
|
switch (op_) {
|
|
|
|
|
case 'E': /* === */
|
|
|
|
|
case 'N': /* !== */
|
|
|
|
|
if (lp->expr_type() == IVL_VT_REAL ||
|
|
|
|
|
rp->expr_type() == IVL_VT_REAL) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< human_readable_op(op_)
|
2009-02-14 03:25:54 +01:00
|
|
|
<< " operator may not have REAL operands." << endl;
|
|
|
|
|
des->errors += 1;
|
2008-10-30 03:34:44 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetEBComp*tmp = new NetEBComp(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
bool flag = tmp->set_width(1);
|
|
|
|
|
if (flag == false) {
|
|
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
"expression bit width of comparison != 1." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tmp;
|
2006-10-30 06:44:49 +01:00
|
|
|
}
|
|
|
|
|
|
2008-10-30 04:31:26 +01:00
|
|
|
unsigned PEBLogic::test_width(Design*des, NetScope*scope,
|
|
|
|
|
unsigned min, unsigned lval,
|
|
|
|
|
ivl_variable_type_t&expr_type_out,
|
|
|
|
|
bool&unsized_flag)
|
|
|
|
|
{
|
|
|
|
|
expr_type_ = IVL_VT_LOGIC;
|
|
|
|
|
expr_width_ = 1;
|
|
|
|
|
expr_type_out = expr_type_;
|
|
|
|
|
return expr_width_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr*PEBLogic::elaborate_expr(Design*des, NetScope*scope,
|
|
|
|
|
int expr_width_dummp, bool sys_task_arg) const
|
|
|
|
|
{
|
|
|
|
|
assert(left_);
|
|
|
|
|
assert(right_);
|
|
|
|
|
|
|
|
|
|
// The left and right expressions are self-determined and
|
|
|
|
|
// independent. Run their test_width methods independently. We
|
|
|
|
|
// don't need the widths here, but we do need the expressions
|
|
|
|
|
// to calculate their self-determined width and type.
|
|
|
|
|
|
|
|
|
|
bool left_flag = false;
|
|
|
|
|
ivl_variable_type_t left_type = IVL_VT_NO_TYPE;
|
|
|
|
|
left_->test_width(des, scope, 0, 0, left_type, left_flag);
|
|
|
|
|
|
|
|
|
|
bool right_flag = false;
|
|
|
|
|
ivl_variable_type_t right_type = IVL_VT_NO_TYPE;
|
|
|
|
|
right_->test_width(des, scope, 0, 0, right_type, right_flag);
|
|
|
|
|
|
|
|
|
|
NetExpr*lp = elab_and_eval(des, scope, left_, -1);
|
|
|
|
|
NetExpr*rp = elab_and_eval(des, scope, right_, -1);
|
|
|
|
|
if ((lp == 0) || (rp == 0)) {
|
|
|
|
|
delete lp;
|
|
|
|
|
delete rp;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lp = condition_reduce(lp);
|
|
|
|
|
rp = condition_reduce(rp);
|
|
|
|
|
|
|
|
|
|
NetEBLogic*tmp = new NetEBLogic(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->set_width(1);
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-28 23:40:25 +01:00
|
|
|
unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope,
|
2008-09-27 07:20:11 +02:00
|
|
|
unsigned min, unsigned lval,
|
2008-10-20 19:06:04 +02:00
|
|
|
ivl_variable_type_t&expr_type__,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&unsized_flag)
|
2006-10-30 06:44:49 +01:00
|
|
|
{
|
2008-11-28 23:40:25 +01:00
|
|
|
unsigned wid_left = left_->test_width(des,scope,min, lval, expr_type__, unsized_flag);
|
2006-10-30 06:44:49 +01:00
|
|
|
|
|
|
|
|
// The right expression is self-determined and has no impact
|
|
|
|
|
// on the expression size that is generated.
|
|
|
|
|
|
2008-08-27 06:33:24 +02:00
|
|
|
if (wid_left < min)
|
|
|
|
|
wid_left = min;
|
|
|
|
|
if (wid_left < lval)
|
|
|
|
|
wid_left = lval;
|
|
|
|
|
|
2008-11-28 23:40:25 +01:00
|
|
|
if (unsized_flag
|
|
|
|
|
&& type_is_vectorable(expr_type__)
|
|
|
|
|
&& wid_left > 0
|
|
|
|
|
&& wid_left < integer_width) {
|
2008-09-21 04:23:54 +02:00
|
|
|
wid_left = integer_width;
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
2008-11-28 23:40:25 +01:00
|
|
|
<< "Test width of unsized " << human_readable_op(op_)
|
2008-09-21 04:23:54 +02:00
|
|
|
<< " is padded to compiler integer width=" << wid_left
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type_ = expr_type__;
|
2008-10-19 07:00:22 +02:00
|
|
|
expr_width_ = wid_left;
|
|
|
|
|
|
2008-10-15 04:36:48 +02:00
|
|
|
// Run a test-width on the shift amount so that its types are
|
|
|
|
|
// worked out for elaboration later on. We don't need the
|
|
|
|
|
// value now.
|
|
|
|
|
ivl_variable_type_t rtype = IVL_VT_NO_TYPE;
|
|
|
|
|
bool rflag = false;
|
|
|
|
|
unsigned wid_right = right_->test_width(des, scope, 0, 0, rtype, rflag);
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
2008-11-28 23:40:25 +01:00
|
|
|
<< "Test width of exponent of " << op_ << " expression "
|
2008-10-15 04:36:48 +02:00
|
|
|
<< "returns wid=" << wid_right << ", type=" << rtype
|
|
|
|
|
<< ", flag=" << rflag << endl;
|
|
|
|
|
|
2008-10-19 07:00:22 +02:00
|
|
|
return expr_width_;
|
2006-10-30 06:44:49 +01:00
|
|
|
}
|
|
|
|
|
|
2008-11-28 23:40:25 +01:00
|
|
|
NetExpr*PEBLeftWidth::elaborate_expr(Design*des, NetScope*scope,
|
|
|
|
|
int expr_wid, bool sys_task_arg) const
|
2008-09-21 04:23:54 +02:00
|
|
|
{
|
|
|
|
|
assert(left_);
|
|
|
|
|
assert(right_);
|
|
|
|
|
|
|
|
|
|
NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, false);
|
2008-11-28 23:40:25 +01:00
|
|
|
if (expr_wid > 0 && lp->expr_width() < (unsigned)expr_wid) {
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Pad left operand of " << human_readable_op(op_)
|
|
|
|
|
<< " to " << expr_wid << "." << endl;
|
|
|
|
|
lp = pad_to_width(lp, expr_wid, *this);
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-21 04:23:54 +02:00
|
|
|
NetExpr*rp = right_->elaborate_expr(des, scope, -1, false);
|
|
|
|
|
if ((lp == 0) || (rp == 0)) {
|
|
|
|
|
delete lp;
|
|
|
|
|
delete rp;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-28 23:40:25 +01:00
|
|
|
eval_expr(lp);
|
|
|
|
|
eval_expr(rp);
|
|
|
|
|
|
|
|
|
|
return elaborate_expr_leaf(des, lp, rp, expr_wid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr*PEBPower::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp,
|
|
|
|
|
int expr_wid) const
|
|
|
|
|
{
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": debug: elaborate expression "
|
|
|
|
|
<< *this << " expr_wid=" << expr_wid << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr*tmp = new NetEBPow(op_, lp, rp);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr*PEBShift::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp,
|
|
|
|
|
int expr_wid) const
|
|
|
|
|
{
|
|
|
|
|
NetExpr*tmp = 0;
|
|
|
|
|
|
|
|
|
|
switch (op_) {
|
|
|
|
|
case 'l':
|
|
|
|
|
tmp = elaborate_expr_base_lshift_(des, lp, rp, expr_wid);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'r': // >>
|
|
|
|
|
case 'R': // >>>
|
|
|
|
|
tmp = elaborate_expr_base_rshift_(des, lp, rp, expr_wid);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "Unexpected opcode " << human_readable_op(op_)
|
|
|
|
|
<< " in PEBShift::elaborate_expr_leaf." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-21 04:23:54 +02:00
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-02 05:38:20 +02:00
|
|
|
unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope,
|
|
|
|
|
unsigned min, unsigned lval,
|
2008-10-20 19:06:04 +02:00
|
|
|
ivl_variable_type_t&expr_type__,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&unsized_flag)
|
2007-10-02 05:38:20 +02:00
|
|
|
{
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
|
|
|
|
|
if (name=="$signed"|| name=="$unsigned") {
|
|
|
|
|
PExpr*expr = parms_[0];
|
|
|
|
|
if (expr == 0)
|
|
|
|
|
return 0;
|
2008-10-19 07:00:22 +02:00
|
|
|
|
2010-01-17 15:20:18 +01:00
|
|
|
// The argument width is self-determined.
|
|
|
|
|
expr_width_ = expr->test_width(des, scope, 0, 0, expr_type__, unsized_flag);
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type_ = expr_type__;
|
2008-10-19 07:00:22 +02:00
|
|
|
|
2010-01-17 15:20:18 +01:00
|
|
|
// The result width is context dependent.
|
|
|
|
|
if (expr_width_ > min)
|
|
|
|
|
min = expr_width_;
|
|
|
|
|
|
2007-10-02 05:38:20 +02:00
|
|
|
if (debug_elaborate)
|
2010-01-17 15:20:18 +01:00
|
|
|
cerr << get_fileline() << ": debug: $signed/$unsigned"
|
|
|
|
|
<< " argument width = " << expr_width_
|
|
|
|
|
<< ", result width = " << min << "." << endl;
|
|
|
|
|
|
|
|
|
|
return min;
|
2007-10-02 05:38:20 +02:00
|
|
|
}
|
|
|
|
|
|
2008-10-11 05:42:07 +02:00
|
|
|
// Run through the arguments of the system function and make
|
|
|
|
|
// sure their widths/types are calculated. They are all self-
|
|
|
|
|
// determined.
|
|
|
|
|
for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) {
|
|
|
|
|
PExpr*expr = parms_[idx];
|
|
|
|
|
ivl_variable_type_t sub_type = IVL_VT_NO_TYPE;
|
|
|
|
|
bool flag = false;
|
|
|
|
|
unsigned wid = expr->test_width(des,scope,0,0,sub_type,flag);
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: test_width"
|
|
|
|
|
<< " of " << name << " argument " << idx+1
|
|
|
|
|
<< " returns type=" << sub_type
|
|
|
|
|
<< ", wid=" << wid << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-27 07:20:11 +02:00
|
|
|
if (name=="$sizeof" || name=="$bits") {
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: test_width"
|
|
|
|
|
<< " of $sizeof/$bits returns test_width"
|
|
|
|
|
<< " of compiler integer." << endl;
|
|
|
|
|
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = IVL_VT_BOOL;
|
|
|
|
|
expr_width_= integer_width;
|
|
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-10-11 05:42:07 +02:00
|
|
|
return expr_width_;
|
2008-09-27 07:20:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (name=="$is_signed") {
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: test_width"
|
|
|
|
|
<< " of $is_signed returns test_width"
|
|
|
|
|
<< " of 1." << endl;
|
|
|
|
|
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = IVL_VT_BOOL;
|
|
|
|
|
expr_width_ = 1;
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-10-11 05:42:07 +02:00
|
|
|
return expr_width_;
|
2008-09-27 07:20:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the return type of the system function by looking it up
|
|
|
|
|
in the sfunc_table. */
|
|
|
|
|
const struct sfunc_return_type*sfunc_info
|
|
|
|
|
= lookup_sys_func(peek_tail_name(path_));
|
|
|
|
|
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = sfunc_info->type;
|
|
|
|
|
expr_width_ = sfunc_info->wid;
|
|
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-09-27 07:20:11 +02:00
|
|
|
|
2007-10-02 05:38:20 +02:00
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: test_width "
|
2007-10-02 05:38:20 +02:00
|
|
|
<< "of system function " << name
|
2008-10-11 05:42:07 +02:00
|
|
|
<< " returns wid=" << expr_width_
|
|
|
|
|
<< ", type=" << expr_type_ << "." << endl;
|
2008-09-27 07:20:11 +02:00
|
|
|
|
2008-10-11 05:42:07 +02:00
|
|
|
return expr_width_;
|
2007-10-02 05:38:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned PECallFunction::test_width(Design*des, NetScope*scope,
|
|
|
|
|
unsigned min, unsigned lval,
|
2008-10-20 19:06:04 +02:00
|
|
|
ivl_variable_type_t&expr_type__,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&unsized_flag)
|
2007-10-02 05:38:20 +02:00
|
|
|
{
|
|
|
|
|
if (peek_tail_name(path_)[0] == '$')
|
2008-10-20 19:06:04 +02:00
|
|
|
return test_width_sfunc_(des, scope, min, lval, expr_type__, unsized_flag);
|
2007-10-02 05:38:20 +02:00
|
|
|
|
2008-09-26 05:37:18 +02:00
|
|
|
// The width of user defined functions depends only on the
|
|
|
|
|
// width of the return value. The arguments are entirely
|
|
|
|
|
// self-determined.
|
2007-10-02 05:38:20 +02:00
|
|
|
NetFuncDef*def = des->find_function(scope, path_);
|
|
|
|
|
if (def == 0) {
|
2008-11-03 06:46:27 +01:00
|
|
|
// If this is an access function, then the width and
|
|
|
|
|
// type are known by definition.
|
|
|
|
|
if (find_access_function(path_)) {
|
|
|
|
|
expr_type_ = IVL_VT_REAL;
|
|
|
|
|
expr_width_ = 1;
|
|
|
|
|
expr_type__ = expr_type_;
|
|
|
|
|
return expr_width_;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-02 05:38:20 +02:00
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: test_width "
|
2007-10-02 05:38:20 +02:00
|
|
|
<< "cannot find definition of " << path_
|
|
|
|
|
<< " in " << scope_path(scope) << "." << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetScope*dscope = def->scope();
|
|
|
|
|
assert(dscope);
|
|
|
|
|
|
|
|
|
|
if (NetNet*res = dscope->find_signal(dscope->basename())) {
|
2008-10-19 07:00:22 +02:00
|
|
|
expr_type_ = res->data_type();
|
2007-10-02 05:38:20 +02:00
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: test_width "
|
2007-10-02 05:38:20 +02:00
|
|
|
<< "of function returns width " << res->vector_width()
|
2008-10-20 19:06:04 +02:00
|
|
|
<< ", type=" << expr_type_
|
2007-10-02 05:38:20 +02:00
|
|
|
<< "." << endl;
|
2008-09-26 05:37:18 +02:00
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
if (! type_is_vectorable(expr_type__))
|
2008-09-26 05:37:18 +02:00
|
|
|
unsized_flag = true;
|
|
|
|
|
|
2008-10-19 07:00:22 +02:00
|
|
|
expr_width_ = res->vector_width();
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-10-19 07:00:22 +02:00
|
|
|
return expr_width_;
|
2007-10-02 05:38:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, 0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2006-10-30 06:44:49 +01:00
|
|
|
|
2010-01-17 15:20:18 +01:00
|
|
|
NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, int wid, bool signed_flag) const
|
|
|
|
|
{
|
|
|
|
|
/* If the expression is a const, then replace it with a new
|
|
|
|
|
const. This is a more efficient result. */
|
|
|
|
|
if (NetEConst*tmp = dynamic_cast<NetEConst*>(expr)) {
|
|
|
|
|
tmp->cast_signed(signed_flag);
|
|
|
|
|
if (wid > (int)(tmp->expr_width())) {
|
|
|
|
|
verinum oval = pad_to_width(tmp->value(), wid);
|
|
|
|
|
tmp = new NetEConst(oval);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
delete expr;
|
|
|
|
|
}
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wid < 0)
|
|
|
|
|
wid = expr->expr_width();
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: cast to " << wid
|
|
|
|
|
<< " bits" << endl;
|
|
|
|
|
|
|
|
|
|
NetESelect*tmp = new NetESelect(expr, 0, wid);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->cast_signed(signed_flag);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2001-02-09 06:44:23 +01:00
|
|
|
/*
|
|
|
|
|
* Given a call to a system function, generate the proper expression
|
|
|
|
|
* nodes to represent the call in the netlist. Since we don't support
|
|
|
|
|
* size_tf functions, make assumptions about widths based on some
|
|
|
|
|
* known function names.
|
|
|
|
|
*/
|
2007-10-10 05:46:17 +02:00
|
|
|
NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_wid) const
|
1999-09-25 04:57:29 +02:00
|
|
|
{
|
2001-12-31 01:08:14 +01:00
|
|
|
|
2010-01-17 15:20:18 +01:00
|
|
|
/* Catch the special case that the system function is the $signed
|
|
|
|
|
function. Its argument will be evaluated as a self-determined
|
|
|
|
|
expression. */
|
2007-05-24 06:07:11 +02:00
|
|
|
if (strcmp(peek_tail_name(path_), "$signed") == 0) {
|
2008-07-27 23:22:19 +02:00
|
|
|
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: The $signed() function "
|
2001-12-31 01:08:14 +01:00
|
|
|
<< "takes exactly one(1) argument." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PExpr*expr = parms_[0];
|
2010-01-17 15:20:18 +01:00
|
|
|
NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, true);
|
|
|
|
|
|
|
|
|
|
return cast_to_width_(sub, expr_wid, true);
|
2001-12-31 01:08:14 +01:00
|
|
|
}
|
2010-01-17 15:20:18 +01:00
|
|
|
/* As above, for the $unsigned function. */
|
2007-05-24 06:07:11 +02:00
|
|
|
if (strcmp(peek_tail_name(path_), "$unsigned") == 0) {
|
2008-07-27 23:22:19 +02:00
|
|
|
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: The $unsigned() function "
|
2004-08-28 17:42:11 +02:00
|
|
|
<< "takes exactly one(1) argument." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PExpr*expr = parms_[0];
|
2010-01-17 15:20:18 +01:00
|
|
|
NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, true);
|
2007-10-10 05:46:17 +02:00
|
|
|
|
2010-01-17 15:20:18 +01:00
|
|
|
return cast_to_width_(sub, expr_wid, false);
|
2004-08-28 17:42:11 +02:00
|
|
|
}
|
2001-12-31 01:08:14 +01:00
|
|
|
|
2002-05-05 23:11:49 +02:00
|
|
|
/* Interpret the internal $sizeof system function to return
|
|
|
|
|
the bit width of the sub-expression. The value of the
|
|
|
|
|
sub-expression is not used, so the expression itself can be
|
|
|
|
|
deleted. */
|
2007-05-24 06:07:11 +02:00
|
|
|
if ((strcmp(peek_tail_name(path_), "$sizeof") == 0)
|
|
|
|
|
|| (strcmp(peek_tail_name(path_), "$bits") == 0)) {
|
2008-07-27 23:22:19 +02:00
|
|
|
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: The $bits() function "
|
2002-05-05 23:11:49 +02:00
|
|
|
<< "takes exactly one(1) argument." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
if (strcmp(peek_tail_name(path_), "$sizeof") == 0)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": warning: $sizeof is deprecated."
|
2002-05-24 02:44:54 +02:00
|
|
|
<< " Use $bits() instead." << endl;
|
|
|
|
|
|
2002-05-05 23:11:49 +02:00
|
|
|
PExpr*expr = parms_[0];
|
2007-07-18 03:05:48 +02:00
|
|
|
ivl_assert(*this, expr);
|
|
|
|
|
|
|
|
|
|
/* Elaborate the sub-expression to get its
|
|
|
|
|
self-determined width, and save that width. Then
|
|
|
|
|
delete the expression because we don't really want
|
2008-09-04 18:41:51 +02:00
|
|
|
the expression itself. */
|
2007-07-18 03:05:48 +02:00
|
|
|
long sub_expr_width = 0;
|
|
|
|
|
if (NetExpr*tmp = expr->elaborate_expr(des, scope, -1, true)) {
|
|
|
|
|
sub_expr_width = tmp->expr_width();
|
|
|
|
|
delete tmp;
|
|
|
|
|
}
|
2002-05-05 23:11:49 +02:00
|
|
|
|
2008-08-20 18:40:16 +02:00
|
|
|
verinum val ( (uint64_t)sub_expr_width, 8*sizeof(unsigned));
|
2007-07-18 03:05:48 +02:00
|
|
|
NetEConst*sub = new NetEConst(val);
|
2002-05-05 23:11:49 +02:00
|
|
|
sub->set_line(*this);
|
|
|
|
|
|
|
|
|
|
return sub;
|
|
|
|
|
}
|
|
|
|
|
|
2004-08-26 05:51:51 +02:00
|
|
|
/* Interpret the internal $is_signed system function to return
|
|
|
|
|
a single bit flag -- 1 if the expression is signed, 0
|
|
|
|
|
otherwise. The subexpression is elaborated but not
|
|
|
|
|
evaluated. */
|
2007-05-24 06:07:11 +02:00
|
|
|
if (strcmp(peek_tail_name(path_), "$is_signed") == 0) {
|
2008-07-27 23:22:19 +02:00
|
|
|
if ((parms_.size() != 1) || (parms_[0] == 0)) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: The $is_signed() function "
|
2004-08-26 05:51:51 +02:00
|
|
|
<< "takes exactly one(1) argument." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PExpr*expr = parms_[0];
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*sub = expr->elaborate_expr(des, scope, -1, true);
|
2004-08-26 05:51:51 +02:00
|
|
|
|
|
|
|
|
verinum val (sub->has_sign()? verinum::V1 : verinum::V0, 1);
|
|
|
|
|
delete sub;
|
|
|
|
|
|
|
|
|
|
sub = new NetEConst(val);
|
|
|
|
|
sub->set_line(*this);
|
|
|
|
|
|
|
|
|
|
return sub;
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-15 05:46:28 +01:00
|
|
|
/* Get the return type of the system function by looking it up
|
|
|
|
|
in the sfunc_table. */
|
2004-03-09 05:29:42 +01:00
|
|
|
const struct sfunc_return_type*sfunc_info
|
2007-05-24 06:07:11 +02:00
|
|
|
= lookup_sys_func(peek_tail_name(path_));
|
2000-05-04 05:37:58 +02:00
|
|
|
|
2005-07-11 18:56:50 +02:00
|
|
|
ivl_variable_type_t sfunc_type = sfunc_info->type;
|
2004-03-09 05:29:42 +01:00
|
|
|
unsigned wid = sfunc_info->wid;
|
2000-05-04 05:37:58 +02:00
|
|
|
|
2001-02-09 06:44:23 +01:00
|
|
|
|
|
|
|
|
/* How many parameters are there? The Verilog language allows
|
|
|
|
|
empty parameters in certain contexts, so the parser will
|
|
|
|
|
allow things like func(1,,3). It will also cause func() to
|
|
|
|
|
be interpreted as a single empty parameter.
|
|
|
|
|
|
|
|
|
|
Functions cannot really take empty parameters, but the
|
2003-01-27 06:09:17 +01:00
|
|
|
case ``func()'' is the same as no parameters at all. So
|
2001-02-09 06:44:23 +01:00
|
|
|
catch that special case here. */
|
2008-07-27 23:22:19 +02:00
|
|
|
unsigned nparms = parms_.size();
|
2001-02-09 06:44:23 +01:00
|
|
|
if ((nparms == 1) && (parms_[0] == 0))
|
|
|
|
|
nparms = 0;
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
NetESFunc*fun = new NetESFunc(peek_tail_name(path_), sfunc_type,
|
2003-03-15 05:46:28 +01:00
|
|
|
wid, nparms);
|
2007-12-22 23:19:45 +01:00
|
|
|
fun->set_line(*this);
|
2004-06-17 18:06:18 +02:00
|
|
|
if (sfunc_info->signed_flag)
|
|
|
|
|
fun->cast_signed(true);
|
2001-02-09 06:44:23 +01:00
|
|
|
|
|
|
|
|
/* Now run through the expected parameters. If we find that
|
2001-02-10 21:29:39 +01:00
|
|
|
there are missing parameters, print an error message.
|
|
|
|
|
|
|
|
|
|
While we're at it, try to evaluate the function parameter
|
|
|
|
|
expression as much as possible, and use the reduced
|
|
|
|
|
expression if one is created. */
|
2001-02-09 06:44:23 +01:00
|
|
|
|
|
|
|
|
unsigned missing_parms = 0;
|
|
|
|
|
for (unsigned idx = 0 ; idx < nparms ; idx += 1) {
|
2000-05-07 20:20:07 +02:00
|
|
|
PExpr*expr = parms_[idx];
|
2001-02-09 06:44:23 +01:00
|
|
|
if (expr) {
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*tmp1 = expr->elaborate_expr(des, scope, -1, true);
|
2008-03-08 03:51:50 +01:00
|
|
|
eval_expr(tmp1);
|
|
|
|
|
fun->parm(idx, tmp1);
|
2001-02-09 06:44:23 +01:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
missing_parms += 1;
|
|
|
|
|
fun->parm(idx, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (missing_parms > 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: The function "
|
2007-05-24 06:07:11 +02:00
|
|
|
<< peek_tail_name(path_)
|
2001-02-09 06:44:23 +01:00
|
|
|
<< " has been called with empty parameters." << endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : Verilog doesn't allow "
|
2001-02-09 06:44:23 +01:00
|
|
|
<< "passing empty parameters to functions." << endl;
|
|
|
|
|
des->errors += 1;
|
2000-05-07 20:20:07 +02:00
|
|
|
}
|
2000-05-04 05:37:58 +02:00
|
|
|
|
2000-05-07 20:20:07 +02:00
|
|
|
return fun;
|
1999-09-25 04:57:29 +02:00
|
|
|
}
|
|
|
|
|
|
2008-07-31 03:01:41 +02:00
|
|
|
NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope,
|
2008-11-04 06:10:10 +01:00
|
|
|
ivl_nature_t nature) const
|
2008-07-31 03:01:41 +02:00
|
|
|
{
|
2008-08-05 05:54:05 +02:00
|
|
|
// An access function must have 1 or 2 arguments.
|
|
|
|
|
ivl_assert(*this, parms_.size()==2 || parms_.size()==1);
|
|
|
|
|
|
|
|
|
|
NetBranch*branch = 0;
|
|
|
|
|
|
|
|
|
|
if (parms_.size() == 1) {
|
|
|
|
|
PExpr*arg1 = parms_[0];
|
|
|
|
|
PEIdent*arg_ident = dynamic_cast<PEIdent*> (arg1);
|
|
|
|
|
ivl_assert(*this, arg_ident);
|
|
|
|
|
|
|
|
|
|
const pform_name_t&path = arg_ident->path();
|
|
|
|
|
ivl_assert(*this, path.size()==1);
|
|
|
|
|
perm_string name = peek_tail_name(path);
|
|
|
|
|
|
|
|
|
|
NetNet*sig = scope->find_signal(name);
|
|
|
|
|
ivl_assert(*this, sig);
|
|
|
|
|
|
2008-11-02 17:10:41 +01:00
|
|
|
ivl_discipline_t dis = sig->get_discipline();
|
2008-08-05 05:54:05 +02:00
|
|
|
ivl_assert(*this, dis);
|
|
|
|
|
ivl_assert(*this, nature == dis->potential() || nature == dis->flow());
|
|
|
|
|
|
2008-11-10 06:42:12 +01:00
|
|
|
NetNet*gnd = des->find_discipline_reference(dis, scope);
|
|
|
|
|
|
2008-11-11 06:19:30 +01:00
|
|
|
if ( (branch = find_existing_implicit_branch(sig, gnd)) ) {
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Re-use implicit branch from "
|
|
|
|
|
<< branch->get_fileline() << endl;
|
|
|
|
|
} else {
|
|
|
|
|
branch = new NetBranch(dis);
|
|
|
|
|
branch->set_line(*this);
|
|
|
|
|
connect(branch->pin(0), sig->pin(0));
|
|
|
|
|
connect(branch->pin(1), gnd->pin(0));
|
|
|
|
|
|
|
|
|
|
des->add_branch(branch);
|
|
|
|
|
join_island(branch);
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Create implicit branch." << endl;
|
|
|
|
|
|
|
|
|
|
}
|
2008-08-05 05:54:05 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
ivl_assert(*this, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetEAccess*tmp = new NetEAccess(branch, nature);
|
2008-07-31 03:01:41 +02:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope,
|
|
|
|
|
int expr_wid, bool) const
|
1999-09-25 04:57:29 +02:00
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
if (peek_tail_name(path_)[0] == '$')
|
2007-10-10 05:46:17 +02:00
|
|
|
return elaborate_sfunc_(des, scope, expr_wid);
|
1999-09-25 04:57:29 +02:00
|
|
|
|
2001-12-03 05:47:14 +01:00
|
|
|
NetFuncDef*def = des->find_function(scope, path_);
|
1999-09-25 04:57:29 +02:00
|
|
|
if (def == 0) {
|
2008-07-31 03:01:41 +02:00
|
|
|
// Not a user defined function. Maybe it is an access
|
2008-11-03 06:46:27 +01:00
|
|
|
// function for a nature? If so then elaborate it that
|
|
|
|
|
// way.
|
2008-11-04 06:10:10 +01:00
|
|
|
ivl_nature_t access_nature = find_access_function(path_);
|
2008-11-03 06:46:27 +01:00
|
|
|
if (access_nature)
|
|
|
|
|
return elaborate_access_func_(des, scope, access_nature);
|
2008-07-31 03:01:41 +02:00
|
|
|
|
2009-02-28 03:58:36 +01:00
|
|
|
// We do not currently support constant user function and
|
|
|
|
|
// this is where things fail because of that, though we
|
|
|
|
|
// don't know for sure so we need to display both messages.
|
|
|
|
|
if (need_constant_expr) {
|
|
|
|
|
cerr << get_fileline() << ": sorry: constant user "
|
|
|
|
|
"functions are not currently supported: "
|
|
|
|
|
<< path_ << "()." << endl << " or" << endl;
|
|
|
|
|
}
|
|
|
|
|
cerr << get_fileline() << ": error: No function " << path_
|
|
|
|
|
<< " in this context (" << scope_path(scope) << ")." << endl;
|
1999-09-25 04:57:29 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-07-31 03:01:41 +02:00
|
|
|
ivl_assert(*this, def);
|
2000-03-08 05:36:53 +01:00
|
|
|
|
2001-12-03 05:47:14 +01:00
|
|
|
NetScope*dscope = def->scope();
|
2008-07-31 03:01:41 +02:00
|
|
|
ivl_assert(*this, dscope);
|
2000-03-08 05:36:53 +01:00
|
|
|
|
2002-03-09 03:10:22 +01:00
|
|
|
if (! check_call_matches_definition_(des, dscope))
|
|
|
|
|
return 0;
|
2001-01-13 23:20:08 +01:00
|
|
|
|
2008-07-27 23:22:19 +02:00
|
|
|
unsigned parms_count = parms_.size();
|
2001-01-13 23:20:08 +01:00
|
|
|
if ((parms_count == 1) && (parms_[0] == 0))
|
|
|
|
|
parms_count = 0;
|
|
|
|
|
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-30 23:28:35 +02:00
|
|
|
|
2001-01-13 23:20:08 +01:00
|
|
|
svector<NetExpr*> parms (parms_count);
|
1999-09-25 04:57:29 +02:00
|
|
|
|
2000-03-08 05:36:53 +01:00
|
|
|
/* Elaborate the input expressions for the function. This is
|
|
|
|
|
done in the scope of the function call, and not the scope
|
|
|
|
|
of the function being called. The scope of the called
|
|
|
|
|
function is elaborated when the definition is elaborated. */
|
|
|
|
|
|
2001-02-09 06:44:23 +01:00
|
|
|
unsigned missing_parms = 0;
|
1999-09-25 04:57:29 +02:00
|
|
|
for (unsigned idx = 0 ; idx < parms.count() ; idx += 1) {
|
2001-01-13 23:20:08 +01:00
|
|
|
PExpr*tmp = parms_[idx];
|
2001-02-09 06:44:23 +01:00
|
|
|
if (tmp) {
|
2008-09-26 05:37:18 +02:00
|
|
|
parms[idx] = elaborate_rval_expr(des, scope,
|
|
|
|
|
def->port(idx)->data_type(),
|
|
|
|
|
def->port(idx)->vector_width(),
|
|
|
|
|
tmp);
|
2009-09-12 04:59:11 +02:00
|
|
|
if (NetEEvent*evt = dynamic_cast<NetEEvent*> (parms[idx])) {
|
|
|
|
|
cerr << evt->get_fileline() << ": error: An event '"
|
|
|
|
|
<< evt->event()->name() << "' can not be a user "
|
|
|
|
|
"function argument." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
2007-10-10 05:46:17 +02:00
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug:"
|
2007-10-10 05:46:17 +02:00
|
|
|
<< " function " << path_
|
|
|
|
|
<< " arg " << (idx+1)
|
2008-09-26 05:37:18 +02:00
|
|
|
<< " argwid=" << parms[idx]->expr_width()
|
2007-10-10 05:46:17 +02:00
|
|
|
<< ": " << *parms[idx] << endl;
|
2001-02-09 06:44:23 +01:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
missing_parms += 1;
|
|
|
|
|
parms[idx] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (missing_parms > 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: The function " << path_
|
2001-02-09 06:44:23 +01:00
|
|
|
<< " has been called with empty parameters." << endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : Verilog doesn't allow "
|
2001-02-09 06:44:23 +01:00
|
|
|
<< "passing empty parameters to functions." << endl;
|
|
|
|
|
des->errors += 1;
|
1999-09-25 04:57:29 +02:00
|
|
|
}
|
|
|
|
|
|
2000-03-08 05:36:53 +01:00
|
|
|
|
|
|
|
|
/* Look for the return value signal for the called
|
|
|
|
|
function. This return value is a magic signal in the scope
|
|
|
|
|
of the function, that has the name of the function. The
|
2001-06-23 21:53:03 +02:00
|
|
|
function code assigns to this signal to return a value.
|
2000-03-08 05:36:53 +01:00
|
|
|
|
2001-06-23 21:53:03 +02:00
|
|
|
dscope, in this case, is the scope of the function, so the
|
|
|
|
|
return value is the name within that scope. */
|
|
|
|
|
|
2004-06-01 01:34:36 +02:00
|
|
|
if (NetNet*res = dscope->find_signal(dscope->basename())) {
|
|
|
|
|
NetESignal*eres = new NetESignal(res);
|
2008-02-22 23:51:53 +01:00
|
|
|
NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms);
|
2007-03-06 06:22:49 +01:00
|
|
|
func->set_line(*this);
|
|
|
|
|
func->cast_signed(res->get_signed());
|
2004-06-01 01:34:36 +02:00
|
|
|
return func;
|
|
|
|
|
}
|
|
|
|
|
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: Unable to locate "
|
2004-06-01 01:34:36 +02:00
|
|
|
"function return value for " << path_
|
2007-06-02 05:42:12 +02:00
|
|
|
<< " in " << dscope->basename() << "." << endl;
|
2004-06-01 01:34:36 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
1999-09-25 04:57:29 +02:00
|
|
|
}
|
|
|
|
|
|
2008-10-19 07:00:22 +02:00
|
|
|
unsigned PEConcat::test_width(Design*des, NetScope*scope,
|
|
|
|
|
unsigned min, unsigned lval,
|
2008-10-20 19:06:04 +02:00
|
|
|
ivl_variable_type_t&expr_type__,
|
2008-10-19 07:00:22 +02:00
|
|
|
bool&unsized_flag)
|
|
|
|
|
{
|
|
|
|
|
expr_type_ = IVL_VT_LOGIC;
|
|
|
|
|
|
2008-11-12 06:09:19 +01:00
|
|
|
unsigned count_width = 0;
|
2008-11-19 01:52:05 +01:00
|
|
|
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
|
|
|
|
|
tested_widths_[idx] = parms_[idx]->test_width(des, scope, 0, 0, expr_type__, unsized_flag);
|
|
|
|
|
count_width += tested_widths_[idx];
|
|
|
|
|
}
|
2008-11-12 06:09:19 +01:00
|
|
|
|
|
|
|
|
if (repeat_) {
|
2009-02-17 20:17:32 +01:00
|
|
|
unsigned repeat_count = 1;
|
|
|
|
|
|
|
|
|
|
// The repeat expression is self-determined and
|
|
|
|
|
// its own type.
|
2008-11-12 06:09:19 +01:00
|
|
|
ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE;
|
|
|
|
|
bool tmp_flag = false;
|
|
|
|
|
repeat_->test_width(des, scope, 0, 0, tmp_type, tmp_flag);
|
|
|
|
|
|
2009-02-17 20:17:32 +01:00
|
|
|
// Try to evaluate the repeat expression now, so
|
|
|
|
|
// that we can give the caller an accurate
|
|
|
|
|
// expression width.
|
2009-09-26 02:23:53 +02:00
|
|
|
NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1);
|
|
|
|
|
if (NetEConst*tmp_c = dynamic_cast<NetEConst*> (tmp)) {
|
2009-02-17 20:17:32 +01:00
|
|
|
repeat_count = tmp_c->value().as_ulong();
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// Gack! Can't evaluate expression yet!
|
|
|
|
|
// Unfortunately, it is possible that this
|
|
|
|
|
// expression may turn out to be constant later in
|
|
|
|
|
// elaboration, so we can't really get away with
|
|
|
|
|
// reporting an error.
|
|
|
|
|
repeat_count = 1;
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "CONCAT MISSING TEST_WIDTH WHEN REPEAT IS PRESENT!"
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
count_width *= repeat_count;
|
2008-11-12 06:09:19 +01:00
|
|
|
}
|
2008-10-19 07:00:22 +02:00
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-10-19 07:00:22 +02:00
|
|
|
unsized_flag = false;
|
2008-11-12 06:09:19 +01:00
|
|
|
return count_width;
|
2008-10-19 07:00:22 +02:00
|
|
|
}
|
|
|
|
|
|
2008-05-01 03:48:36 +02:00
|
|
|
// Keep track of the concatenation/repeat depth.
|
|
|
|
|
static int concat_depth = 0;
|
1999-09-20 04:21:10 +02:00
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope,
|
|
|
|
|
int expr_wid, bool) const
|
2000-01-01 07:18:00 +01:00
|
|
|
{
|
2008-05-01 03:48:36 +02:00
|
|
|
concat_depth += 1;
|
2002-05-05 23:11:49 +02:00
|
|
|
NetExpr* repeat = 0;
|
2000-01-01 07:18:00 +01:00
|
|
|
|
2007-08-10 07:08:54 +02:00
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Elaborate expr=" << *this
|
2007-08-10 07:08:54 +02:00
|
|
|
<< ", expr_wid=" << expr_wid << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-01 07:18:00 +01:00
|
|
|
/* If there is a repeat expression, then evaluate the constant
|
|
|
|
|
value and set the repeat count. */
|
|
|
|
|
if (repeat_) {
|
2009-09-26 02:23:53 +02:00
|
|
|
need_constant_expr = true;
|
|
|
|
|
NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1);
|
|
|
|
|
need_constant_expr = false;
|
|
|
|
|
assert(tmp);
|
2009-02-14 03:25:54 +01:00
|
|
|
|
|
|
|
|
if (tmp->expr_type() == IVL_VT_REAL) {
|
2009-04-09 23:48:16 +02:00
|
|
|
cerr << tmp->get_fileline() << ": error: Concatenation "
|
2009-02-14 03:25:54 +01:00
|
|
|
<< "repeat expression can not be REAL." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-02-10 21:29:39 +01:00
|
|
|
NetEConst*rep = dynamic_cast<NetEConst*>(tmp);
|
|
|
|
|
|
|
|
|
|
if (rep == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: "
|
2009-04-09 23:48:16 +02:00
|
|
|
"Concatenation repeat expression cannot be evaluated."
|
2000-01-01 07:18:00 +01:00
|
|
|
<< endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : The expression is: "
|
2001-02-10 21:29:39 +01:00
|
|
|
<< *tmp << endl;
|
2000-01-01 07:18:00 +01:00
|
|
|
des->errors += 1;
|
2009-02-14 03:25:54 +01:00
|
|
|
return 0;
|
2000-01-01 07:18:00 +01:00
|
|
|
}
|
|
|
|
|
|
2008-05-26 20:14:59 +02:00
|
|
|
if (!rep->value().is_defined()) {
|
|
|
|
|
cerr << get_fileline() << ": error: Concatenation repeat "
|
|
|
|
|
<< "may not be undefined (" << rep->value()
|
|
|
|
|
<< ")." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
concat_depth -= 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-25 23:17:47 +01:00
|
|
|
if (rep->value().is_negative()) {
|
|
|
|
|
cerr << get_fileline() << ": error: Concatenation repeat "
|
|
|
|
|
<< "may not be negative (" << rep->value().as_long()
|
|
|
|
|
<< ")." << endl;
|
|
|
|
|
des->errors += 1;
|
2008-05-01 03:48:36 +02:00
|
|
|
concat_depth -= 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rep->value().is_zero() && concat_depth < 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: Concatenation repeat "
|
|
|
|
|
<< "may not be zero in this context." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
concat_depth -= 1;
|
2008-03-25 23:17:47 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2002-05-05 23:11:49 +02:00
|
|
|
repeat = rep;
|
2000-01-01 07:18:00 +01:00
|
|
|
}
|
|
|
|
|
|
2002-06-14 23:38:41 +02:00
|
|
|
unsigned wid_sum = 0;
|
2010-03-22 06:00:44 +01:00
|
|
|
unsigned parm_cnt = 0;
|
|
|
|
|
svector<NetExpr*> parms(parms_.count());
|
2002-06-14 23:38:41 +02:00
|
|
|
|
2000-01-01 07:18:00 +01:00
|
|
|
/* Elaborate all the parameters and attach them to the concat node. */
|
|
|
|
|
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
|
2004-01-21 05:57:40 +01:00
|
|
|
if (parms_[idx] == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Missing expression "
|
2004-01-21 05:57:40 +01:00
|
|
|
<< (idx+1) << " of concatenation list." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-01 07:18:00 +01:00
|
|
|
assert(parms_[idx]);
|
2008-11-19 01:52:05 +01:00
|
|
|
NetExpr*ex = elab_and_eval(des, scope, parms_[idx],
|
|
|
|
|
tested_widths_[idx], 0);
|
2000-01-01 07:18:00 +01:00
|
|
|
if (ex == 0) continue;
|
2000-09-26 07:05:58 +02:00
|
|
|
|
2000-01-01 07:18:00 +01:00
|
|
|
ex->set_line(*parms_[idx]);
|
2000-09-26 07:05:58 +02:00
|
|
|
|
2009-04-02 19:03:07 +02:00
|
|
|
if (ex->expr_type() == IVL_VT_REAL) {
|
2009-02-14 03:25:54 +01:00
|
|
|
cerr << ex->get_fileline() << ": error: "
|
2009-04-09 23:48:16 +02:00
|
|
|
<< "Concatenation operand can not be real: "
|
2009-04-02 19:03:07 +02:00
|
|
|
<< *parms_[idx] << endl;
|
2009-02-14 03:25:54 +01:00
|
|
|
des->errors += 1;
|
2009-04-02 19:03:07 +02:00
|
|
|
continue;
|
2009-02-14 03:25:54 +01:00
|
|
|
}
|
|
|
|
|
|
2009-04-02 19:03:07 +02:00
|
|
|
if (! ex->has_width()) {
|
2009-02-14 03:25:54 +01:00
|
|
|
cerr << ex->get_fileline() << ": error: "
|
2009-04-09 23:48:16 +02:00
|
|
|
<< "Concatenation operand \"" << *parms_[idx]
|
|
|
|
|
<< "\" has indefinite width." << endl;
|
2000-09-26 07:05:58 +02:00
|
|
|
des->errors += 1;
|
2009-04-02 19:03:07 +02:00
|
|
|
continue;
|
2000-09-26 07:05:58 +02:00
|
|
|
}
|
|
|
|
|
|
2010-03-22 06:00:44 +01:00
|
|
|
/* We are going to ignore zero width constants. */
|
|
|
|
|
if ((ex->expr_width() == 0) && dynamic_cast<NetEConst*>(ex)) {
|
|
|
|
|
parms[idx] = 0;
|
|
|
|
|
} else {
|
|
|
|
|
parms[idx] = ex;
|
|
|
|
|
parm_cnt += 1;
|
|
|
|
|
}
|
2002-06-14 23:38:41 +02:00
|
|
|
wid_sum += ex->expr_width();
|
2010-03-22 06:00:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the empty concat expression. */
|
|
|
|
|
NetEConcat*tmp = new NetEConcat(parm_cnt, repeat);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
|
|
|
|
|
/* Remove any zero width constants. */
|
|
|
|
|
unsigned off = 0;
|
|
|
|
|
for (unsigned idx = 0 ; idx < parm_cnt ; idx += 1) {
|
|
|
|
|
while (parms[off+idx] == 0) off += 1;
|
|
|
|
|
tmp->set(idx, parms[off+idx]);
|
2000-01-01 07:18:00 +01:00
|
|
|
}
|
|
|
|
|
|
2002-06-14 23:38:41 +02:00
|
|
|
tmp->set_width(wid_sum * tmp->repeat());
|
|
|
|
|
|
2008-05-01 03:48:36 +02:00
|
|
|
if (wid_sum == 0 && concat_depth < 2) {
|
|
|
|
|
cerr << get_fileline() << ": error: Concatenation may not "
|
|
|
|
|
<< "have zero width in this context." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
concat_depth -= 1;
|
2009-04-02 19:03:07 +02:00
|
|
|
delete tmp;
|
2008-05-01 03:48:36 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
concat_depth -= 1;
|
2000-01-01 07:18:00 +01:00
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-26 05:37:18 +02:00
|
|
|
/*
|
|
|
|
|
* Floating point literals are not vectorable. It's not particularly
|
|
|
|
|
* clear what to do about an actual width to return, but whatever the
|
|
|
|
|
* width, it is unsigned.
|
|
|
|
|
*
|
|
|
|
|
* Absent any better idea, we call all real valued results a width of 1.
|
|
|
|
|
*/
|
|
|
|
|
unsigned PEFNumber::test_width(Design*des, NetScope*scope,
|
|
|
|
|
unsigned min, unsigned lval,
|
2008-10-20 19:06:04 +02:00
|
|
|
ivl_variable_type_t&expr_type__,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&unsized_flag)
|
2008-09-26 05:37:18 +02:00
|
|
|
{
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = IVL_VT_REAL;
|
|
|
|
|
expr_width_ = 1;
|
2009-12-28 02:11:45 +01:00
|
|
|
unsized_flag = false;
|
2008-10-11 05:42:07 +02:00
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-09-26 05:37:18 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr* PEFNumber::elaborate_expr(Design*des, NetScope*scope, int, bool) const
|
2001-01-15 00:04:55 +01:00
|
|
|
{
|
2003-01-26 22:15:58 +01:00
|
|
|
NetECReal*tmp = new NetECReal(*value_);
|
|
|
|
|
tmp->set_line(*this);
|
2007-06-30 06:10:37 +02:00
|
|
|
tmp->set_width(1U, false);
|
2003-01-26 22:15:58 +01:00
|
|
|
return tmp;
|
2001-01-15 00:04:55 +01:00
|
|
|
}
|
|
|
|
|
|
2006-11-04 07:19:24 +01:00
|
|
|
/*
|
|
|
|
|
* Given that the msb_ and lsb_ are part select expressions, this
|
|
|
|
|
* function calculates their values. Note that this method does *not*
|
|
|
|
|
* convert the values to canonical form.
|
|
|
|
|
*/
|
|
|
|
|
bool PEIdent::calculate_parts_(Design*des, NetScope*scope,
|
2009-01-02 01:20:41 +01:00
|
|
|
long&msb, long&lsb, bool&defined) const
|
2006-11-04 07:19:24 +01:00
|
|
|
{
|
2009-01-02 01:20:41 +01:00
|
|
|
defined = true;
|
2007-05-24 06:07:11 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
ivl_assert(*this, !name_tail.index.empty());
|
|
|
|
|
|
|
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
|
|
|
|
ivl_assert(*this, index_tail.sel == index_component_t::SEL_PART);
|
|
|
|
|
ivl_assert(*this, index_tail.msb && index_tail.lsb);
|
2006-11-04 07:19:24 +01:00
|
|
|
|
2008-10-11 05:42:07 +02:00
|
|
|
ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE;
|
|
|
|
|
bool tmp_flag = false;
|
|
|
|
|
int msb_wid = index_tail.msb->test_width(des, scope, 0, 0, tmp_type, tmp_flag);
|
|
|
|
|
|
|
|
|
|
tmp_type = IVL_VT_NO_TYPE;
|
|
|
|
|
tmp_flag = false;
|
|
|
|
|
int lsb_wid = index_tail.lsb->test_width(des, scope, 0, 0, tmp_type, tmp_flag);
|
|
|
|
|
|
2006-11-04 07:19:24 +01:00
|
|
|
/* This handles part selects. In this case, there are
|
|
|
|
|
two bit select expressions, and both must be
|
|
|
|
|
constant. Evaluate them and pass the results back to
|
|
|
|
|
the caller. */
|
2009-03-03 05:32:13 +01:00
|
|
|
need_constant_expr = true;
|
2008-10-11 05:42:07 +02:00
|
|
|
NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, lsb_wid);
|
2009-03-03 05:32:13 +01:00
|
|
|
need_constant_expr = false;
|
2006-11-04 07:19:24 +01:00
|
|
|
NetEConst*lsb_c = dynamic_cast<NetEConst*>(lsb_ex);
|
|
|
|
|
if (lsb_c == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << index_tail.lsb->get_fileline() << ": error: "
|
2006-11-04 07:19:24 +01:00
|
|
|
"Part select expressions must be constant."
|
|
|
|
|
<< endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << index_tail.lsb->get_fileline() << ": : "
|
2007-05-24 06:07:11 +02:00
|
|
|
"This lsb expression violates the rule: "
|
|
|
|
|
<< *index_tail.lsb << endl;
|
2006-11-04 07:19:24 +01:00
|
|
|
des->errors += 1;
|
2008-10-04 16:30:34 +02:00
|
|
|
/* Attempt to recover from error. */
|
|
|
|
|
lsb = 0;
|
|
|
|
|
} else {
|
2009-01-02 01:20:41 +01:00
|
|
|
if (! lsb_c->value().is_defined())
|
|
|
|
|
defined = false;
|
2008-10-04 16:30:34 +02:00
|
|
|
lsb = lsb_c->value().as_long();
|
2006-11-04 07:19:24 +01:00
|
|
|
}
|
2006-10-30 06:44:49 +01:00
|
|
|
|
2009-03-03 05:32:13 +01:00
|
|
|
need_constant_expr = true;
|
2008-10-11 05:42:07 +02:00
|
|
|
NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, msb_wid);
|
2009-03-03 05:32:13 +01:00
|
|
|
need_constant_expr = false;
|
2006-11-04 07:19:24 +01:00
|
|
|
NetEConst*msb_c = dynamic_cast<NetEConst*>(msb_ex);
|
|
|
|
|
if (msb_c == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << index_tail.msb->get_fileline() << ": error: "
|
2006-11-04 07:19:24 +01:00
|
|
|
"Part select expressions must be constant."
|
|
|
|
|
<< endl;
|
2008-10-04 16:30:34 +02:00
|
|
|
cerr << index_tail.msb->get_fileline() << ": : "
|
|
|
|
|
"This msb expression violates the rule: "
|
|
|
|
|
<< *index_tail.msb << endl;
|
2006-11-04 07:19:24 +01:00
|
|
|
des->errors += 1;
|
2008-10-04 16:30:34 +02:00
|
|
|
/* Attempt to recover from error. */
|
|
|
|
|
msb = lsb;
|
|
|
|
|
} else {
|
2009-01-02 01:20:41 +01:00
|
|
|
if (! msb_c->value().is_defined())
|
|
|
|
|
defined = false;
|
2008-10-04 16:30:34 +02:00
|
|
|
msb = msb_c->value().as_long();
|
2006-11-04 07:19:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete msb_ex;
|
|
|
|
|
delete lsb_ex;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope,
|
|
|
|
|
unsigned long&wid) const
|
2006-10-30 06:44:49 +01:00
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
ivl_assert(*this, !name_tail.index.empty());
|
|
|
|
|
|
|
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
|
|
|
|
ivl_assert(*this, index_tail.lsb && index_tail.msb);
|
|
|
|
|
|
2006-11-04 07:19:24 +01:00
|
|
|
bool flag = true;
|
|
|
|
|
|
|
|
|
|
/* Calculate the width expression (in the lsb_ position)
|
|
|
|
|
first. If the expression is not constant, error but guess 1
|
|
|
|
|
so we can keep going and find more errors. */
|
2009-03-03 05:32:13 +01:00
|
|
|
need_constant_expr = true;
|
2007-05-24 06:07:11 +02:00
|
|
|
NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1);
|
2009-03-03 05:32:13 +01:00
|
|
|
need_constant_expr = false;
|
2006-11-04 07:19:24 +01:00
|
|
|
NetEConst*wid_c = dynamic_cast<NetEConst*>(wid_ex);
|
|
|
|
|
|
|
|
|
|
if (wid_c == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Indexed part width must be "
|
2006-11-04 07:19:24 +01:00
|
|
|
<< "constant. Expression in question is..." << endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : " << *wid_ex << endl;
|
2006-11-04 07:19:24 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
flag = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wid = wid_c? wid_c->value().as_ulong() : 1;
|
|
|
|
|
delete wid_ex;
|
|
|
|
|
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-09 04:29:00 +02:00
|
|
|
/*
|
|
|
|
|
* When we know that this is an indexed part select (up or down) this
|
|
|
|
|
* method calculates the up/down base, as far at it can be calculated.
|
|
|
|
|
*/
|
|
|
|
|
NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
ivl_assert(*this, !name_tail.index.empty());
|
|
|
|
|
|
|
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
|
|
|
|
ivl_assert(*this, index_tail.lsb != 0);
|
|
|
|
|
ivl_assert(*this, index_tail.msb != 0);
|
|
|
|
|
|
2008-11-27 00:37:38 +01:00
|
|
|
probe_expr_width(des, scope, index_tail.msb);
|
2008-06-09 04:29:00 +02:00
|
|
|
NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-08 16:34:45 +02:00
|
|
|
bool PEIdent::calculate_param_range_(Design*des, NetScope*scope,
|
|
|
|
|
const NetExpr*par_msb, long&par_msv,
|
2010-01-10 04:57:01 +01:00
|
|
|
const NetExpr*par_lsb, long&par_lsv,
|
|
|
|
|
long length) const
|
2008-06-08 16:34:45 +02:00
|
|
|
{
|
|
|
|
|
if (par_msb == 0) {
|
|
|
|
|
// If the parameter doesn't have an explicit range, then
|
2010-01-10 04:57:01 +01:00
|
|
|
// just return range values of [length-1:0].
|
2008-06-08 16:34:45 +02:00
|
|
|
ivl_assert(*this, par_lsb == 0);
|
2010-01-10 04:57:01 +01:00
|
|
|
par_msv = length-1;
|
2008-06-08 16:34:45 +02:00
|
|
|
par_lsv = 0;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const NetEConst*tmp = dynamic_cast<const NetEConst*> (par_msb);
|
|
|
|
|
ivl_assert(*this, tmp);
|
|
|
|
|
|
|
|
|
|
par_msv = tmp->value().as_long();
|
|
|
|
|
|
|
|
|
|
tmp = dynamic_cast<const NetEConst*> (par_lsb);
|
|
|
|
|
ivl_assert(*this, tmp);
|
|
|
|
|
|
|
|
|
|
par_lsv = tmp->value().as_long();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-19 01:02:58 +01:00
|
|
|
static void probe_index_expr_width(Design*des, NetScope*scope,
|
|
|
|
|
const name_component_t&name)
|
|
|
|
|
{
|
|
|
|
|
for (list<index_component_t>::const_iterator cur = name.index.begin()
|
|
|
|
|
; cur != name.index.end() ; cur ++) {
|
|
|
|
|
|
|
|
|
|
if (cur->msb)
|
|
|
|
|
probe_expr_width(des, scope, cur->msb);
|
|
|
|
|
if (cur->lsb)
|
|
|
|
|
probe_expr_width(des, scope, cur->lsb);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-04 07:19:24 +01:00
|
|
|
unsigned PEIdent::test_width(Design*des, NetScope*scope,
|
|
|
|
|
unsigned min, unsigned lval,
|
2008-10-20 19:06:04 +02:00
|
|
|
ivl_variable_type_t&expr_type__,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&unsized_flag)
|
2006-11-04 07:19:24 +01:00
|
|
|
{
|
|
|
|
|
NetNet* net = 0;
|
|
|
|
|
const NetExpr*par = 0;
|
|
|
|
|
NetEvent* eve = 0;
|
|
|
|
|
|
|
|
|
|
const NetExpr*ex1, *ex2;
|
|
|
|
|
|
2008-10-28 18:52:39 +01:00
|
|
|
symbol_search(0, des, scope, path_, net, par, eve, ex1, ex2);
|
2006-11-04 07:19:24 +01:00
|
|
|
|
2008-08-27 06:33:24 +02:00
|
|
|
// If there is a part/bit select expression, then process it
|
|
|
|
|
// here. This constrains the results no matter what kind the
|
|
|
|
|
// name is.
|
|
|
|
|
|
|
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
|
2008-10-11 05:42:07 +02:00
|
|
|
if (!name_tail.index.empty()) {
|
2009-02-19 01:02:58 +01:00
|
|
|
probe_index_expr_width(des, scope, name_tail);
|
2008-10-11 05:42:07 +02:00
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
2009-04-28 03:13:19 +02:00
|
|
|
// Skip full array word net selects.
|
|
|
|
|
if (!net || (name_tail.index.size() > net->array_dimensions())) {
|
|
|
|
|
use_sel = index_tail.sel;
|
|
|
|
|
}
|
2008-10-11 05:42:07 +02:00
|
|
|
}
|
2008-08-27 06:33:24 +02:00
|
|
|
|
|
|
|
|
unsigned use_width = UINT_MAX;
|
|
|
|
|
switch (use_sel) {
|
|
|
|
|
case index_component_t::SEL_NONE:
|
|
|
|
|
break;
|
|
|
|
|
case index_component_t::SEL_PART:
|
|
|
|
|
{ long msb, lsb;
|
2009-01-02 01:20:41 +01:00
|
|
|
bool parts_defined;
|
|
|
|
|
calculate_parts_(des, scope, msb, lsb, parts_defined);
|
|
|
|
|
if (parts_defined)
|
|
|
|
|
use_width = 1 + ((msb>lsb)? (msb-lsb) : (lsb-msb));
|
|
|
|
|
else
|
|
|
|
|
use_width = UINT_MAX;
|
2008-08-27 06:33:24 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case index_component_t::SEL_IDX_UP:
|
|
|
|
|
case index_component_t::SEL_IDX_DO:
|
|
|
|
|
{ unsigned long tmp = 0;
|
|
|
|
|
calculate_up_do_width_(des, scope, tmp);
|
|
|
|
|
use_width = tmp;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case index_component_t::SEL_BIT:
|
2008-11-27 00:37:38 +01:00
|
|
|
{ ivl_assert(*this, !name_tail.index.empty());
|
|
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
|
|
|
|
ivl_assert(*this, index_tail.msb);
|
|
|
|
|
}
|
|
|
|
|
use_width = 1;
|
|
|
|
|
break;
|
2008-08-27 06:33:24 +02:00
|
|
|
default:
|
|
|
|
|
ivl_assert(*this, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-19 07:00:22 +02:00
|
|
|
if (use_width != UINT_MAX) {
|
|
|
|
|
expr_type_ = IVL_VT_LOGIC; // Assume bit/parts selects are logic
|
|
|
|
|
expr_width_ = max(use_width, min);
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-10-19 07:00:22 +02:00
|
|
|
return expr_width_;
|
|
|
|
|
}
|
2008-08-27 06:33:24 +02:00
|
|
|
|
|
|
|
|
// The width of a signal expression is the width of the signal.
|
2008-09-27 07:20:11 +02:00
|
|
|
if (net != 0) {
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = net->data_type();
|
|
|
|
|
expr_width_= max(net->vector_width(), (unsigned long)min);
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-10-11 05:42:07 +02:00
|
|
|
return expr_width_;
|
2008-09-27 07:20:11 +02:00
|
|
|
}
|
2008-08-27 06:33:24 +02:00
|
|
|
|
|
|
|
|
// The width of a parameter name is the width of the range for
|
|
|
|
|
// the parameter name, if a range is declared. Otherwise, the
|
|
|
|
|
// width is undefined.
|
|
|
|
|
if (par != 0) {
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = par->expr_type();
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-08-27 06:33:24 +02:00
|
|
|
if (ex1) {
|
|
|
|
|
ivl_assert(*this, ex2);
|
|
|
|
|
const NetEConst*ex1_const = dynamic_cast<const NetEConst*> (ex1);
|
|
|
|
|
const NetEConst*ex2_const = dynamic_cast<const NetEConst*> (ex2);
|
|
|
|
|
ivl_assert(*this, ex1_const && ex2_const);
|
|
|
|
|
|
|
|
|
|
long msb = ex1_const->value().as_long();
|
|
|
|
|
long lsb = ex2_const->value().as_long();
|
|
|
|
|
if (msb >= lsb)
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_width_ = msb - lsb + 1;
|
2008-08-27 06:33:24 +02:00
|
|
|
else
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_width_ = lsb - msb + 1;
|
|
|
|
|
return expr_width_;
|
2008-08-27 06:33:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is a parameter. If it is sized (meaning it was
|
2008-10-21 18:00:48 +02:00
|
|
|
// declared with range expressions) then the range
|
2008-08-27 06:33:24 +02:00
|
|
|
// expressions would have been caught above. So if we
|
|
|
|
|
// got there there we know this is an unsized constant.
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_width_ = par->expr_width();
|
2008-08-27 06:33:24 +02:00
|
|
|
unsized_flag = true;
|
2008-10-11 05:42:07 +02:00
|
|
|
return expr_width_;
|
2006-11-04 07:19:24 +01:00
|
|
|
}
|
|
|
|
|
|
2008-10-19 07:00:22 +02:00
|
|
|
// Not a net, and not a parameter? Give up on the type, but
|
|
|
|
|
// set the width that we collected.
|
|
|
|
|
expr_type_ = IVL_VT_NO_TYPE;
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_width_ = min;
|
2008-10-19 07:00:22 +02:00
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2006-10-30 06:44:49 +01:00
|
|
|
return min;
|
|
|
|
|
}
|
|
|
|
|
|
2001-12-29 21:41:30 +01:00
|
|
|
/*
|
|
|
|
|
* Elaborate an identifier in an expression. The identifier can be a
|
|
|
|
|
* parameter name, a signal name or a memory name. It can also be a
|
|
|
|
|
* scope name (Return a NetEScope) but only certain callers can use
|
|
|
|
|
* scope names. However, we still support it here.
|
|
|
|
|
*
|
|
|
|
|
* Function names are not handled here, they are detected by the
|
|
|
|
|
* parser and are elaborated by PECallFunction.
|
|
|
|
|
*
|
|
|
|
|
* The signal name may be escaped, but that affects nothing here.
|
|
|
|
|
*/
|
2002-04-13 04:33:17 +02:00
|
|
|
NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
|
2006-06-02 06:48:49 +02:00
|
|
|
int expr_wid, bool sys_task_arg) const
|
1999-09-20 04:21:10 +02:00
|
|
|
{
|
2000-03-08 05:36:53 +01:00
|
|
|
assert(scope);
|
2003-09-19 05:30:04 +02:00
|
|
|
|
|
|
|
|
NetNet* net = 0;
|
|
|
|
|
const NetExpr*par = 0;
|
|
|
|
|
NetEvent* eve = 0;
|
|
|
|
|
|
2005-11-27 06:56:20 +01:00
|
|
|
const NetExpr*ex1, *ex2;
|
|
|
|
|
|
2008-10-28 18:52:39 +01:00
|
|
|
NetScope*found_in = symbol_search(this, des, scope, path_,
|
2007-01-16 06:44:14 +01:00
|
|
|
net, par, eve,
|
2005-11-27 06:56:20 +01:00
|
|
|
ex1, ex2);
|
1999-09-20 04:21:10 +02:00
|
|
|
|
|
|
|
|
// If the identifier name is a parameter name, then return
|
|
|
|
|
// a reference to the parameter expression.
|
2005-10-04 06:09:25 +02:00
|
|
|
if (par != 0)
|
2008-09-21 04:23:54 +02:00
|
|
|
return elaborate_expr_param_(des, scope, par, found_in, ex1, ex2, expr_wid);
|
2003-03-11 00:40:53 +01:00
|
|
|
|
1999-09-20 04:21:10 +02:00
|
|
|
|
|
|
|
|
// If the identifier names a signal (a register or wire)
|
|
|
|
|
// then create a NetESignal node to handle it.
|
2005-11-10 14:28:11 +01:00
|
|
|
if (net != 0)
|
2007-01-16 06:44:14 +01:00
|
|
|
return elaborate_expr_net(des, scope, net, found_in, sys_task_arg);
|
1999-09-20 04:21:10 +02:00
|
|
|
|
2003-04-22 06:48:29 +02:00
|
|
|
// If the identifier is a named event.
|
|
|
|
|
// is a variable reference.
|
2003-09-19 05:30:04 +02:00
|
|
|
if (eve != 0) {
|
|
|
|
|
NetEEvent*tmp = new NetEEvent(eve);
|
2003-04-22 06:48:29 +02:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-12 07:05:03 +02:00
|
|
|
// Hmm... maybe this is a genvar? This is only possible while
|
|
|
|
|
// processing generate blocks, but then the genvar_tmp will be
|
|
|
|
|
// set in the scope.
|
2007-05-24 06:07:11 +02:00
|
|
|
if (path_.size() == 1
|
2006-04-12 07:05:03 +02:00
|
|
|
&& scope->genvar_tmp.str()
|
2007-05-24 06:07:11 +02:00
|
|
|
&& strcmp(peek_tail_name(path_), scope->genvar_tmp) == 0) {
|
2007-06-26 05:33:40 +02:00
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: " << path_
|
2007-06-26 05:33:40 +02:00
|
|
|
<< " is genvar with value " << scope->genvar_tmp_val
|
|
|
|
|
<< "." << endl;
|
2006-04-12 07:05:03 +02:00
|
|
|
verinum val (scope->genvar_tmp_val);
|
|
|
|
|
NetEConst*tmp = new NetEConst(val);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-28 02:29:49 +02:00
|
|
|
// A specparam? Look up the name to see if it is a
|
|
|
|
|
// specparam. If we find it, then turn it into a NetEConst
|
2006-09-28 06:35:18 +02:00
|
|
|
// value and return that. Of course, this does not apply if
|
|
|
|
|
// specify blocks are disabled.
|
|
|
|
|
|
|
|
|
|
if (gn_specify_blocks_flag) {
|
2006-10-03 07:06:00 +02:00
|
|
|
map<perm_string,NetScope::spec_val_t>::const_iterator specp;
|
2007-05-24 06:07:11 +02:00
|
|
|
perm_string key = peek_tail_name(path_);
|
|
|
|
|
if (path_.size() == 1
|
2006-09-28 06:35:18 +02:00
|
|
|
&& ((specp = scope->specparams.find(key)) != scope->specparams.end())) {
|
2006-10-03 07:06:00 +02:00
|
|
|
NetScope::spec_val_t value = (*specp).second;
|
2006-10-15 05:25:57 +02:00
|
|
|
NetExpr*tmp = 0;
|
2006-10-03 07:06:00 +02:00
|
|
|
switch (value.type) {
|
|
|
|
|
case IVL_VT_BOOL:
|
|
|
|
|
tmp = new NetEConst(verinum(value.integer));
|
|
|
|
|
break;
|
|
|
|
|
case IVL_VT_REAL:
|
|
|
|
|
tmp = new NetECReal(verireal(value.real_val));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
assert(tmp);
|
2006-09-28 06:35:18 +02:00
|
|
|
tmp->set_line(*this);
|
2007-06-26 05:33:40 +02:00
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: " << path_
|
2007-06-26 05:33:40 +02:00
|
|
|
<< " is a specparam" << endl;
|
2006-09-28 06:35:18 +02:00
|
|
|
return tmp;
|
|
|
|
|
}
|
2006-09-28 02:29:49 +02:00
|
|
|
}
|
|
|
|
|
|
2007-06-26 05:33:40 +02:00
|
|
|
// At this point we've exhausted all the possibilities that
|
|
|
|
|
// are not scopes. If this is not a system task argument, then
|
|
|
|
|
// it cannot be a scope name, so give up.
|
|
|
|
|
|
|
|
|
|
if (! sys_task_arg) {
|
|
|
|
|
// I cannot interpret this identifier. Error message.
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Unable to bind wire/reg/memory "
|
2007-06-26 05:33:40 +02:00
|
|
|
"`" << path_ << "' in `" << scope_path(scope) << "'" << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-07-30 00:22:40 +02:00
|
|
|
// Finally, if this is a scope name, then return that. Look
|
|
|
|
|
// first to see if this is a name of a local scope. Failing
|
2003-01-27 06:09:17 +01:00
|
|
|
// that, search globally for a hierarchical name.
|
2007-06-02 05:42:12 +02:00
|
|
|
if ((path_.size() == 1)) {
|
|
|
|
|
hname_t use_name ( peek_tail_name(path_) );
|
|
|
|
|
if (NetScope*nsc = scope->child(use_name)) {
|
2001-12-03 05:47:14 +01:00
|
|
|
NetEScope*tmp = new NetEScope(nsc);
|
|
|
|
|
tmp->set_line(*this);
|
2007-06-26 05:33:40 +02:00
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Found scope "
|
2007-06-26 05:33:40 +02:00
|
|
|
<< use_name << " in scope " << scope->basename()
|
|
|
|
|
<< endl;
|
|
|
|
|
|
2001-12-03 05:47:14 +01:00
|
|
|
return tmp;
|
|
|
|
|
}
|
2007-06-02 05:42:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list<hname_t> spath = eval_scope_path(des, scope, path_);
|
2001-07-30 00:22:40 +02:00
|
|
|
|
2007-06-26 05:33:40 +02:00
|
|
|
ivl_assert(*this, spath.size() == path_.size());
|
|
|
|
|
|
2003-03-25 04:00:04 +01:00
|
|
|
// Try full hierarchical scope name.
|
2007-06-02 05:42:12 +02:00
|
|
|
if (NetScope*nsc = des->find_scope(spath)) {
|
1999-11-30 05:54:01 +01:00
|
|
|
NetEScope*tmp = new NetEScope(nsc);
|
|
|
|
|
tmp->set_line(*this);
|
2007-06-26 05:33:40 +02:00
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Found scope "
|
2007-06-26 05:33:40 +02:00
|
|
|
<< nsc->basename()
|
|
|
|
|
<< " path=" << path_ << endl;
|
|
|
|
|
|
|
|
|
|
if (! sys_task_arg) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Scope name "
|
2007-06-26 05:33:40 +02:00
|
|
|
<< nsc->basename() << " not allowed here." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-30 05:54:01 +01:00
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-25 04:00:04 +01:00
|
|
|
// Try relative scope name.
|
2007-06-02 05:42:12 +02:00
|
|
|
if (NetScope*nsc = des->find_scope(scope, spath)) {
|
2003-03-25 04:00:04 +01:00
|
|
|
NetEScope*tmp = new NetEScope(nsc);
|
|
|
|
|
tmp->set_line(*this);
|
2007-06-26 05:33:40 +02:00
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Found scope "
|
2007-06-26 05:33:40 +02:00
|
|
|
<< nsc->basename() << " in " << scope_path(scope) << endl;
|
|
|
|
|
|
2003-03-25 04:00:04 +01:00
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
1999-09-20 04:21:10 +02:00
|
|
|
// I cannot interpret this identifier. Error message.
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Unable to bind wire/reg/memory "
|
2007-06-02 05:42:12 +02:00
|
|
|
"`" << path_ << "' in `" << scope_path(scope) << "'" << endl;
|
1999-09-20 04:21:10 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-09 04:29:00 +02:00
|
|
|
static verinum param_part_select_bits(const verinum&par_val, long wid,
|
2009-08-29 01:50:59 +02:00
|
|
|
long lsv)
|
2008-06-09 04:29:00 +02:00
|
|
|
{
|
|
|
|
|
verinum result (verinum::Vx, wid, true);
|
|
|
|
|
|
|
|
|
|
for (long idx = 0 ; idx < wid ; idx += 1) {
|
2009-08-29 01:50:59 +02:00
|
|
|
long off = idx + lsv;
|
2008-06-09 04:29:00 +02:00
|
|
|
if (off < 0)
|
2010-01-10 04:57:01 +01:00
|
|
|
continue;
|
2008-06-09 04:29:00 +02:00
|
|
|
else if (off < (long)par_val.len())
|
|
|
|
|
result.set(idx, par_val.get(off));
|
|
|
|
|
else if (par_val.is_string()) // Pad strings with nulls.
|
|
|
|
|
result.set(idx, verinum::V0);
|
|
|
|
|
else if (par_val.has_len()) // Pad sized parameters with X
|
2010-01-10 04:57:01 +01:00
|
|
|
continue;
|
2008-06-09 04:29:00 +02:00
|
|
|
else // Unsized parameters are "infinite" width.
|
|
|
|
|
result.set(idx, sign_bit(par_val));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the input is a string, and the part select is working on
|
|
|
|
|
// byte boundaries, then make the result into a string.
|
2009-08-29 01:50:59 +02:00
|
|
|
if (par_val.is_string() && (labs(lsv)%8 == 0) && (wid%8 == 0))
|
2008-06-09 04:29:00 +02:00
|
|
|
return result.as_string();
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-08 16:34:45 +02:00
|
|
|
NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope,
|
|
|
|
|
const NetExpr*par,
|
|
|
|
|
NetScope*found_in,
|
|
|
|
|
const NetExpr*par_msb,
|
|
|
|
|
const NetExpr*par_lsb) const
|
|
|
|
|
{
|
|
|
|
|
long msv, lsv;
|
2009-01-02 01:20:41 +01:00
|
|
|
bool parts_defined_flag;
|
|
|
|
|
bool flag = calculate_parts_(des, scope, msv, lsv, parts_defined_flag);
|
2008-06-08 16:34:45 +02:00
|
|
|
if (!flag)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2010-01-10 04:57:01 +01:00
|
|
|
const NetEConst*par_ex = dynamic_cast<const NetEConst*> (par);
|
|
|
|
|
ivl_assert(*this, par_ex);
|
|
|
|
|
|
|
|
|
|
|
2008-06-08 16:34:45 +02:00
|
|
|
long par_msv, par_lsv;
|
2010-01-10 04:57:01 +01:00
|
|
|
if (! calculate_param_range_(des, scope, par_msb, par_msv,
|
|
|
|
|
par_lsb, par_lsv,
|
|
|
|
|
par_ex->value().len())) return 0;
|
2008-06-08 16:34:45 +02:00
|
|
|
|
2009-01-02 01:20:41 +01:00
|
|
|
if (! parts_defined_flag) {
|
2010-01-10 04:57:01 +01:00
|
|
|
if (warn_ob_select) {
|
|
|
|
|
const index_component_t&psel = path_.back().index.back();
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
"Undefined part select [" << *(psel.msb) << ":"
|
|
|
|
|
<< *(psel.lsb) << "] for parameter '" << name
|
|
|
|
|
<< "'." << endl;
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
"Replacing select with a constant 'bx." << endl;
|
|
|
|
|
}
|
2009-01-02 01:20:41 +01:00
|
|
|
|
2010-01-10 04:57:01 +01:00
|
|
|
NetEConst*tmp = new NetEConst(verinum(verinum::Vx, 1, false));
|
2009-01-02 01:20:41 +01:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-10 17:49:36 +02:00
|
|
|
// Notice that the par_msv is not used in this function other
|
|
|
|
|
// than for this test. It is used to tell the direction that
|
2008-06-08 16:34:45 +02:00
|
|
|
// the bits are numbers, so that we can make sure the
|
|
|
|
|
// direction matches the part select direction. After that,
|
|
|
|
|
// we only need the par_lsv.
|
2008-06-10 17:49:36 +02:00
|
|
|
if ((msv>lsv && par_msv<par_lsv) || (msv<lsv && par_msv>=par_lsv)) {
|
2010-01-10 04:57:01 +01:00
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
cerr << get_fileline() << ": error: Part select " << name
|
2008-06-08 16:34:45 +02:00
|
|
|
<< "[" << msv << ":" << lsv << "] is out of order." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long wid = 1 + labs(msv-lsv);
|
|
|
|
|
|
2010-01-10 04:57:01 +01:00
|
|
|
// Watch out for reversed bit numbering. We're making
|
|
|
|
|
// the part select from LSB to MSB.
|
|
|
|
|
long base;
|
|
|
|
|
if (par_msv < par_lsv) {
|
|
|
|
|
base = par_lsv - lsv;
|
|
|
|
|
} else {
|
|
|
|
|
base = lsv - par_lsv;
|
|
|
|
|
}
|
2008-06-08 16:34:45 +02:00
|
|
|
|
2010-01-10 04:57:01 +01:00
|
|
|
if (warn_ob_select) {
|
|
|
|
|
if (base < 0) {
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
cerr << get_fileline() << ": warning: Part select "
|
|
|
|
|
<< "[" << msv << ":" << lsv << "] is selecting "
|
|
|
|
|
"before the parameter " << name << "[";
|
|
|
|
|
if (par_ex->value().has_len()) cerr << par_msv;
|
|
|
|
|
else cerr << "<inf>";
|
|
|
|
|
cerr << ":" << par_lsv << "]." << endl;
|
|
|
|
|
cerr << get_fileline() << ": : Replacing "
|
|
|
|
|
"the out of bound bits with 'bx." << endl;
|
|
|
|
|
}
|
|
|
|
|
if (par_ex->value().has_len() &&
|
|
|
|
|
(base+wid > (long)par->expr_width())) {
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
cerr << get_fileline() << ": warning: Part select "
|
|
|
|
|
<< name << "[" << msv << ":" << lsv << "] is selecting "
|
|
|
|
|
"after the parameter " << name << "[" << par_msv
|
|
|
|
|
<< ":" << par_lsv << "]." << endl;
|
|
|
|
|
cerr << get_fileline() << ": : Replacing "
|
|
|
|
|
"the out of bound bits with 'bx." << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-06-09 04:29:00 +02:00
|
|
|
|
2010-01-10 04:57:01 +01:00
|
|
|
verinum result = param_part_select_bits(par_ex->value(), wid, base);
|
2008-06-09 04:29:00 +02:00
|
|
|
NetEConst*result_ex = new NetEConst(result);
|
|
|
|
|
result_ex->set_line(*this);
|
|
|
|
|
|
|
|
|
|
return result_ex;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-29 01:50:59 +02:00
|
|
|
static void warn_param_ob(long par_msv, long par_lsv, bool defined,
|
|
|
|
|
long par_base, unsigned long wid, long pwid,
|
|
|
|
|
const LineInfo *info, perm_string name, bool up)
|
|
|
|
|
{
|
|
|
|
|
long par_max;
|
|
|
|
|
|
|
|
|
|
if (defined) {
|
|
|
|
|
if (par_msv < par_lsv) par_max = par_lsv-par_msv;
|
|
|
|
|
else par_max = par_msv-par_lsv;
|
|
|
|
|
} else {
|
|
|
|
|
if (pwid < 0) par_max = integer_width;
|
|
|
|
|
else par_max = pwid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Is this a select before the start of the parameter? */
|
|
|
|
|
if (par_base < 0) {
|
|
|
|
|
cerr << info->get_fileline() << ": warning: " << name << "["
|
|
|
|
|
<< par_base;
|
|
|
|
|
if (up) cerr << "+:";
|
|
|
|
|
else cerr << "-:";
|
|
|
|
|
cerr << wid << "] is selecting before vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Is this a select after the end of the parameter? */
|
|
|
|
|
if (par_base + (long)wid - 1 > par_max) {
|
|
|
|
|
cerr << info->get_fileline() << ": warning: " << name << "["
|
|
|
|
|
<< par_base << "+:" << wid << "] is selecting after vector."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-09 04:29:00 +02:00
|
|
|
NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope,
|
|
|
|
|
const NetExpr*par,
|
|
|
|
|
NetScope*found_in,
|
|
|
|
|
const NetExpr*par_msb,
|
|
|
|
|
const NetExpr*par_lsb) const
|
|
|
|
|
{
|
2010-01-10 04:57:01 +01:00
|
|
|
const NetEConst*par_ex = dynamic_cast<const NetEConst*> (par);
|
|
|
|
|
ivl_assert(*this, par_ex);
|
|
|
|
|
|
2008-06-09 04:29:00 +02:00
|
|
|
long par_msv, par_lsv;
|
2009-08-29 01:50:59 +02:00
|
|
|
if(! calculate_param_range_(des, scope, par_msb, par_msv,
|
2010-01-10 04:57:01 +01:00
|
|
|
par_lsb, par_lsv,
|
|
|
|
|
par_ex->value().len())) return 0;
|
2008-06-09 04:29:00 +02:00
|
|
|
|
|
|
|
|
NetExpr*base = calculate_up_do_base_(des, scope);
|
2009-08-29 01:50:59 +02:00
|
|
|
if (base == 0) return 0;
|
2008-06-09 04:29:00 +02:00
|
|
|
|
|
|
|
|
unsigned long wid = 0;
|
|
|
|
|
calculate_up_do_width_(des, scope, wid);
|
2008-06-08 16:34:45 +02:00
|
|
|
|
2008-06-09 04:29:00 +02:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: Calculate part select "
|
|
|
|
|
<< "[" << *base << "+:" << wid << "] from range "
|
|
|
|
|
<< "[" << par_msv << ":" << par_lsv << "]." << endl;
|
|
|
|
|
|
|
|
|
|
// Handle the special case that the base is constant. In this
|
|
|
|
|
// case, just precalculate the entire constant result.
|
|
|
|
|
if (NetEConst*base_c = dynamic_cast<NetEConst*> (base)) {
|
2009-08-29 01:50:59 +02:00
|
|
|
if (! base_c->value().is_defined()) {
|
|
|
|
|
NetEConst *ex;
|
|
|
|
|
ex = new NetEConst(verinum(verinum::Vx, wid, true));
|
|
|
|
|
ex->set_line(*this);
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
cerr << get_fileline() << ": warning: " << name
|
|
|
|
|
<< "['bx+:" << wid
|
|
|
|
|
<< "] is always outside vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
return ex;
|
|
|
|
|
}
|
2008-06-09 04:29:00 +02:00
|
|
|
long lsv = base_c->value().as_long();
|
2009-08-29 01:50:59 +02:00
|
|
|
long par_base = par_lsv;
|
2008-06-09 04:29:00 +02:00
|
|
|
|
|
|
|
|
// Watch out for reversed bit numbering. We're making
|
|
|
|
|
// the part select from LSB to MSB.
|
2009-08-29 01:50:59 +02:00
|
|
|
if (par_msv < par_lsv) {
|
|
|
|
|
par_base = lsv;
|
|
|
|
|
lsv = par_lsv - wid + 1;
|
|
|
|
|
}
|
2008-06-09 04:29:00 +02:00
|
|
|
|
2009-08-29 01:50:59 +02:00
|
|
|
if (warn_ob_select) {
|
|
|
|
|
bool defined = true;
|
|
|
|
|
// Check to see if the parameter has a defined range.
|
|
|
|
|
if (par_msb == 0) {
|
|
|
|
|
assert(par_lsb == 0);
|
|
|
|
|
defined = false;
|
|
|
|
|
}
|
|
|
|
|
// Get the parameter values width.
|
|
|
|
|
long pwid = -1;
|
|
|
|
|
if (par_ex->has_width()) pwid = par_ex->expr_width()-1;
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
warn_param_ob(par_msv, par_lsv, defined, lsv-par_base, wid,
|
|
|
|
|
pwid, this, name, true);
|
|
|
|
|
}
|
2008-06-09 04:29:00 +02:00
|
|
|
verinum result = param_part_select_bits(par_ex->value(), wid,
|
2009-08-29 01:50:59 +02:00
|
|
|
lsv-par_base);
|
2008-06-09 04:29:00 +02:00
|
|
|
NetEConst*result_ex = new NetEConst(result);
|
|
|
|
|
result_ex->set_line(*this);
|
|
|
|
|
return result_ex;
|
2008-06-08 16:34:45 +02:00
|
|
|
}
|
|
|
|
|
|
2009-08-29 01:50:59 +02:00
|
|
|
if (par_msv >= par_lsv) {
|
|
|
|
|
if (par_lsv != 0) base = make_add_expr(base, -par_lsv);
|
|
|
|
|
} else {
|
|
|
|
|
base = make_sub_expr(par_lsv-wid+1, base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr*tmp = par->dup_expr();
|
|
|
|
|
tmp = new NetESelect(tmp, base, wid);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope,
|
|
|
|
|
const NetExpr*par,
|
|
|
|
|
NetScope*found_in,
|
|
|
|
|
const NetExpr*par_msb,
|
|
|
|
|
const NetExpr*par_lsb) const
|
|
|
|
|
{
|
2010-01-10 04:57:01 +01:00
|
|
|
const NetEConst*par_ex = dynamic_cast<const NetEConst*> (par);
|
|
|
|
|
ivl_assert(*this, par_ex);
|
|
|
|
|
|
2009-08-29 01:50:59 +02:00
|
|
|
long par_msv, par_lsv;
|
|
|
|
|
if(! calculate_param_range_(des, scope, par_msb, par_msv,
|
2010-01-10 04:57:01 +01:00
|
|
|
par_lsb, par_lsv,
|
|
|
|
|
par_ex->value().len())) return 0;
|
2009-08-29 01:50:59 +02:00
|
|
|
|
|
|
|
|
NetExpr*base = calculate_up_do_base_(des, scope);
|
|
|
|
|
if (base == 0) return 0;
|
|
|
|
|
|
|
|
|
|
unsigned long wid = 0;
|
|
|
|
|
calculate_up_do_width_(des, scope, wid);
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: Calculate part select "
|
|
|
|
|
<< "[" << *base << "-:" << wid << "] from range "
|
|
|
|
|
<< "[" << par_msv << ":" << par_lsv << "]." << endl;
|
|
|
|
|
|
|
|
|
|
// Handle the special case that the base is constant. In this
|
|
|
|
|
// case, just precalculate the entire constant result.
|
|
|
|
|
if (NetEConst*base_c = dynamic_cast<NetEConst*> (base)) {
|
|
|
|
|
if (! base_c->value().is_defined()) {
|
|
|
|
|
NetEConst *ex;
|
|
|
|
|
ex = new NetEConst(verinum(verinum::Vx, wid, true));
|
|
|
|
|
ex->set_line(*this);
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
cerr << get_fileline() << ": warning: " << name
|
|
|
|
|
<< "['bx-:" << wid
|
|
|
|
|
<< "] is always outside vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
return ex;
|
|
|
|
|
}
|
|
|
|
|
long lsv = base_c->value().as_long();
|
|
|
|
|
long par_base = par_lsv + wid - 1;
|
|
|
|
|
|
|
|
|
|
// Watch out for reversed bit numbering. We're making
|
|
|
|
|
// the part select from LSB to MSB.
|
|
|
|
|
if (par_msv < par_lsv) {
|
|
|
|
|
par_base = lsv;
|
|
|
|
|
lsv = par_lsv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
bool defined = true;
|
|
|
|
|
// Check to see if the parameter has a defined range.
|
|
|
|
|
if (par_msb == 0) {
|
|
|
|
|
assert(par_lsb == 0);
|
|
|
|
|
defined = false;
|
|
|
|
|
}
|
|
|
|
|
// Get the parameter values width.
|
|
|
|
|
long pwid = -1;
|
|
|
|
|
if (par_ex->has_width()) pwid = par_ex->expr_width()-1;
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
warn_param_ob(par_msv, par_lsv, defined, lsv-par_base, wid,
|
|
|
|
|
pwid, this, name, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verinum result = param_part_select_bits(par_ex->value(), wid,
|
|
|
|
|
lsv-par_base);
|
|
|
|
|
NetEConst*result_ex = new NetEConst(result);
|
|
|
|
|
result_ex->set_line(*this);
|
|
|
|
|
return result_ex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (par_msv >= par_lsv) {
|
|
|
|
|
if (long offset = par_lsv+wid-1) {
|
|
|
|
|
base = make_add_expr(base, -offset);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
base = make_sub_expr(par_lsv, base);
|
|
|
|
|
}
|
2008-06-08 16:34:45 +02:00
|
|
|
|
2008-06-09 04:29:00 +02:00
|
|
|
NetExpr*tmp = par->dup_expr();
|
|
|
|
|
tmp = new NetESelect(tmp, base, wid);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
2008-06-08 16:34:45 +02:00
|
|
|
}
|
|
|
|
|
|
2005-11-27 06:56:20 +01:00
|
|
|
/*
|
|
|
|
|
* Handle the case that the identifier is a parameter reference. The
|
|
|
|
|
* parameter expression has already been located for us (as the par
|
|
|
|
|
* argument) so we just need to process the sub-expression.
|
|
|
|
|
*/
|
2008-09-21 04:23:54 +02:00
|
|
|
NetExpr* PEIdent::elaborate_expr_param_(Design*des,
|
|
|
|
|
NetScope*scope,
|
|
|
|
|
const NetExpr*par,
|
|
|
|
|
NetScope*found_in,
|
|
|
|
|
const NetExpr*par_msb,
|
|
|
|
|
const NetExpr*par_lsb,
|
|
|
|
|
int expr_wid) const
|
2005-10-04 06:09:25 +02:00
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
|
|
|
|
|
if (!name_tail.index.empty())
|
|
|
|
|
use_sel = name_tail.index.back().sel;
|
2005-10-04 06:09:25 +02:00
|
|
|
|
2009-04-02 03:31:29 +02:00
|
|
|
if (par->expr_type() == IVL_VT_REAL &&
|
|
|
|
|
use_sel != index_component_t::SEL_NONE) {
|
2009-04-02 20:05:09 +02:00
|
|
|
perm_string name = peek_tail_name(path_);
|
2009-04-02 03:31:29 +02:00
|
|
|
cerr << get_fileline() << ": error: "
|
2009-04-02 20:05:09 +02:00
|
|
|
<< "can not select part of real parameter: " << name << endl;
|
2009-04-02 03:31:29 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-08 16:34:45 +02:00
|
|
|
// NOTE TO SELF: This is the way I want to see this code
|
|
|
|
|
// structured. This closely follows the structure of the
|
|
|
|
|
// elaborate_expr_net_ code, which splits all the various
|
|
|
|
|
// selects to different methods.
|
|
|
|
|
if (use_sel == index_component_t::SEL_PART)
|
|
|
|
|
return elaborate_expr_param_part_(des, scope, par, found_in,
|
|
|
|
|
par_msb, par_lsb);
|
2005-10-04 06:09:25 +02:00
|
|
|
|
2008-06-09 04:29:00 +02:00
|
|
|
if (use_sel == index_component_t::SEL_IDX_UP)
|
|
|
|
|
return elaborate_expr_param_idx_up_(des, scope, par, found_in,
|
|
|
|
|
par_msb, par_lsb);
|
|
|
|
|
|
2009-08-29 01:50:59 +02:00
|
|
|
if (use_sel == index_component_t::SEL_IDX_DO)
|
|
|
|
|
return elaborate_expr_param_idx_do_(des, scope, par, found_in,
|
|
|
|
|
par_msb, par_lsb);
|
|
|
|
|
|
2008-06-08 16:34:45 +02:00
|
|
|
// NOTE TO SELF (continued): The code below should be
|
|
|
|
|
// rewritten in the above format, as I get to it.
|
2005-10-04 06:09:25 +02:00
|
|
|
|
2008-06-08 16:34:45 +02:00
|
|
|
NetExpr*tmp = par->dup_expr();
|
2005-10-04 06:09:25 +02:00
|
|
|
|
2009-08-29 01:50:59 +02:00
|
|
|
if (use_sel == index_component_t::SEL_BIT) {
|
2007-05-24 06:07:11 +02:00
|
|
|
ivl_assert(*this, !name_tail.index.empty());
|
|
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
|
|
|
|
ivl_assert(*this, index_tail.msb);
|
|
|
|
|
ivl_assert(*this, !index_tail.lsb);
|
2005-11-27 06:56:20 +01:00
|
|
|
|
2008-04-09 06:20:49 +02:00
|
|
|
const NetEConst*par_me =dynamic_cast<const NetEConst*>(par_msb);
|
|
|
|
|
const NetEConst*par_le =dynamic_cast<const NetEConst*>(par_lsb);
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, par_me || !par_msb);
|
|
|
|
|
ivl_assert(*this, par_le || !par_lsb);
|
|
|
|
|
ivl_assert(*this, par_me || !par_le);
|
|
|
|
|
|
2005-10-04 06:09:25 +02:00
|
|
|
/* Handle the case where a parameter has a bit
|
|
|
|
|
select attached to it. Generate a NetESelect
|
|
|
|
|
object to select the bit as desired. */
|
2007-05-24 06:07:11 +02:00
|
|
|
NetExpr*mtmp = index_tail.msb->elaborate_expr(des, scope, -1,false);
|
2008-03-08 03:51:50 +01:00
|
|
|
eval_expr(mtmp);
|
2005-10-04 06:09:25 +02:00
|
|
|
|
|
|
|
|
/* Let's first try to get constant values for both
|
|
|
|
|
the parameter and the bit select. If they are
|
|
|
|
|
both constant, then evaluate the bit select and
|
|
|
|
|
return instead a single-bit constant. */
|
|
|
|
|
|
|
|
|
|
NetEConst*le = dynamic_cast<NetEConst*>(tmp);
|
|
|
|
|
NetEConst*re = dynamic_cast<NetEConst*>(mtmp);
|
|
|
|
|
if (le && re) {
|
|
|
|
|
|
2009-01-02 01:20:41 +01:00
|
|
|
// Special case: If the bit select is constant and
|
|
|
|
|
// not fully defined, then we know that the result
|
|
|
|
|
// must be 1'bx.
|
|
|
|
|
if (! re->value().is_defined()) {
|
2010-01-10 04:57:01 +01:00
|
|
|
if (warn_ob_select) {
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
"Constant undefined bit select ["
|
|
|
|
|
<< re->value() << "] for parameter '"
|
|
|
|
|
<< name << "'." << endl;
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
"Replacing select with a constant "
|
|
|
|
|
"1'bx." << endl;
|
|
|
|
|
}
|
2009-01-02 01:20:41 +01:00
|
|
|
NetEConst*res = make_const_x(1);
|
|
|
|
|
res->set_line(*this);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2005-11-27 06:56:20 +01:00
|
|
|
/* Argument and bit select are constant. Calculate
|
|
|
|
|
the final result. */
|
2005-10-04 06:09:25 +02:00
|
|
|
verinum lv = le->value();
|
|
|
|
|
verinum rv = re->value();
|
|
|
|
|
verinum::V rb = verinum::Vx;
|
|
|
|
|
|
2008-04-09 06:20:49 +02:00
|
|
|
long par_mv = lv.len()-1;
|
|
|
|
|
long par_lv = 0;
|
|
|
|
|
if (par_me) {
|
|
|
|
|
par_mv = par_me->value().as_long();
|
|
|
|
|
par_lv = par_le->value().as_long();
|
|
|
|
|
}
|
2008-06-10 18:02:24 +02:00
|
|
|
/* Convert the index to canonical bit address. */
|
2005-10-04 06:09:25 +02:00
|
|
|
long ridx = rv.as_long();
|
2008-04-09 06:20:49 +02:00
|
|
|
if (par_mv >= par_lv) {
|
|
|
|
|
ridx -= par_lv;
|
|
|
|
|
} else {
|
2010-01-10 04:57:01 +01:00
|
|
|
ridx = par_lv - ridx;
|
2008-04-09 06:20:49 +02:00
|
|
|
}
|
|
|
|
|
|
2005-10-04 06:09:25 +02:00
|
|
|
if ((ridx >= 0) && ((unsigned long) ridx < lv.len())) {
|
|
|
|
|
rb = lv[ridx];
|
|
|
|
|
|
|
|
|
|
} else if ((ridx >= 0) && (!lv.has_len())) {
|
|
|
|
|
if (lv.has_sign())
|
|
|
|
|
rb = lv[lv.len()-1];
|
|
|
|
|
else
|
|
|
|
|
rb = verinum::V0;
|
2010-01-10 04:57:01 +01:00
|
|
|
} else {
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
"Constant bit select [" << rv.as_long()
|
|
|
|
|
<< "] is ";
|
|
|
|
|
if (ridx < 0) cerr << "before ";
|
|
|
|
|
else cerr << "after ";
|
|
|
|
|
cerr << name << "[";
|
|
|
|
|
if (lv.has_len()) cerr << par_mv;
|
|
|
|
|
else cerr << "<inf>";
|
|
|
|
|
cerr << ":" << par_lv << "]." << endl;
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
"Replacing select with a constant "
|
|
|
|
|
"1'bx." << endl;
|
|
|
|
|
}
|
2005-10-04 06:09:25 +02:00
|
|
|
}
|
|
|
|
|
|
2010-01-10 04:57:01 +01:00
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
NetEConst*re2 = new NetEConst(verinum(rb, 1));
|
2005-10-04 06:09:25 +02:00
|
|
|
delete tmp;
|
|
|
|
|
delete mtmp;
|
2008-10-20 19:06:04 +02:00
|
|
|
tmp = re2;
|
2005-10-04 06:09:25 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
2005-11-27 06:56:20 +01:00
|
|
|
if (par_me) {
|
|
|
|
|
long par_mv = par_me->value().as_long();
|
|
|
|
|
long par_lv = par_le->value().as_long();
|
|
|
|
|
if (par_mv >= par_lv) {
|
|
|
|
|
mtmp = par_lv
|
|
|
|
|
? make_add_expr(mtmp, 0-par_lv)
|
|
|
|
|
: mtmp;
|
|
|
|
|
} else {
|
|
|
|
|
if (par_lv != 0)
|
|
|
|
|
mtmp = make_add_expr(mtmp, 0-par_mv);
|
|
|
|
|
mtmp = make_sub_expr(par_lv-par_mv, mtmp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The value is constant, but the bit select
|
|
|
|
|
expression is not. Elaborate a NetESelect to
|
|
|
|
|
evaluate the select at run-time. */
|
|
|
|
|
|
2005-10-04 06:09:25 +02:00
|
|
|
NetESelect*stmp = new NetESelect(tmp, mtmp, 1);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp = stmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
/* No bit or part select. Make the constant into a
|
|
|
|
|
NetEConstParam if possible. */
|
|
|
|
|
NetEConst*ctmp = dynamic_cast<NetEConst*>(tmp);
|
|
|
|
|
if (ctmp != 0) {
|
2007-05-24 06:07:11 +02:00
|
|
|
perm_string name = peek_tail_name(path_);
|
2005-10-04 06:09:25 +02:00
|
|
|
NetEConstParam*ptmp
|
|
|
|
|
= new NetEConstParam(found_in, name, ctmp->value());
|
2008-01-23 05:16:43 +01:00
|
|
|
|
2008-09-21 04:23:54 +02:00
|
|
|
if (expr_wid > 0)
|
|
|
|
|
ptmp->set_width((unsigned)expr_wid);
|
|
|
|
|
|
2008-01-23 05:16:43 +01:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Elaborate parameter <" << name
|
|
|
|
|
<< "> as constant " << *ptmp << endl;
|
2005-10-04 06:09:25 +02:00
|
|
|
delete tmp;
|
|
|
|
|
tmp = ptmp;
|
|
|
|
|
}
|
2009-01-16 20:03:03 +01:00
|
|
|
|
|
|
|
|
NetECReal*rtmp = dynamic_cast<NetECReal*>(tmp);
|
|
|
|
|
if (rtmp != 0) {
|
|
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
NetECRealParam*ptmp
|
|
|
|
|
= new NetECRealParam(found_in, name, rtmp->value());
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Elaborate parameter <" << name
|
|
|
|
|
<< "> as constant " << *ptmp << endl;
|
|
|
|
|
delete tmp;
|
|
|
|
|
tmp = ptmp;
|
|
|
|
|
}
|
2005-10-04 06:09:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
/*
|
|
|
|
|
* Handle word selects of vector arrays.
|
|
|
|
|
*/
|
|
|
|
|
NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope,
|
|
|
|
|
NetNet*net, NetScope*found_in,
|
|
|
|
|
bool sys_task_arg) const
|
|
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
|
|
|
|
|
if (name_tail.index.empty() && !sys_task_arg) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Array " << path()
|
2007-03-02 02:55:36 +01:00
|
|
|
<< " Needs an array index here." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
index_component_t index_front;
|
|
|
|
|
if (! name_tail.index.empty()) {
|
|
|
|
|
index_front = name_tail.index.front();
|
|
|
|
|
ivl_assert(*this, index_front.sel != index_component_t::SEL_NONE);
|
|
|
|
|
if (index_front.sel != index_component_t::SEL_BIT) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Array " << path_
|
2007-05-24 06:07:11 +02:00
|
|
|
<< " cannot be indexed by a range." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
ivl_assert(*this, index_front.msb);
|
|
|
|
|
ivl_assert(*this, !index_front.lsb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr*word_index = index_front.sel == index_component_t::SEL_NONE
|
2007-01-16 06:44:14 +01:00
|
|
|
? 0
|
2007-05-24 06:07:11 +02:00
|
|
|
: elab_and_eval(des, scope, index_front.msb, -1);
|
2007-01-16 06:44:14 +01:00
|
|
|
if (word_index == 0 && !sys_task_arg)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (NetEConst*word_addr = dynamic_cast<NetEConst*>(word_index)) {
|
|
|
|
|
long addr = word_addr->value().as_long();
|
|
|
|
|
|
|
|
|
|
// Special case: The index is out of range, so the value
|
|
|
|
|
// of this expression is a 'bx vector the width of a word.
|
|
|
|
|
if (!net->array_index_is_valid(addr)) {
|
2009-07-04 00:37:44 +02:00
|
|
|
cerr << get_fileline() << ": warning: returning 'bx for out "
|
|
|
|
|
"of bounds array access " << net->name()
|
|
|
|
|
<< "[" << addr << "]." << endl;
|
2008-06-07 07:05:17 +02:00
|
|
|
NetEConst*resx = make_const_x(net->vector_width());
|
2007-01-16 06:44:14 +01:00
|
|
|
resx->set_line(*this);
|
|
|
|
|
delete word_index;
|
|
|
|
|
return resx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Recalculate the constant address with the adjusted base.
|
|
|
|
|
unsigned use_addr = net->array_index_to_address(addr);
|
2007-10-10 06:19:38 +02:00
|
|
|
if (addr < 0 || use_addr != (unsigned long)addr) {
|
2008-08-20 18:40:16 +02:00
|
|
|
verinum val ( (uint64_t)use_addr, 8*sizeof(use_addr));
|
2007-01-16 06:44:14 +01:00
|
|
|
NetEConst*tmp = new NetEConst(val);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
delete word_index;
|
|
|
|
|
word_index = tmp;
|
|
|
|
|
}
|
2007-07-22 02:19:24 +02:00
|
|
|
|
|
|
|
|
} else if (word_index) {
|
|
|
|
|
// If there is a non-zero base to the memory, then build an
|
|
|
|
|
// expression to calculate the canonical address.
|
|
|
|
|
if (long base = net->array_first()) {
|
|
|
|
|
|
|
|
|
|
word_index = make_add_expr(word_index, 0-base);
|
2008-03-08 03:51:50 +01:00
|
|
|
eval_expr(word_index);
|
2007-07-22 02:19:24 +02:00
|
|
|
}
|
2007-01-16 06:44:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetESignal*res = new NetESignal(net, word_index);
|
|
|
|
|
res->set_line(*this);
|
|
|
|
|
|
2007-07-06 21:46:32 +02:00
|
|
|
// Detect that the word has a bit/part select as well.
|
2007-05-24 06:07:11 +02:00
|
|
|
|
|
|
|
|
index_component_t::ctype_t word_sel = index_component_t::SEL_NONE;
|
|
|
|
|
if (name_tail.index.size() > 1)
|
|
|
|
|
word_sel = name_tail.index.back().sel;
|
|
|
|
|
|
2009-04-15 01:08:27 +02:00
|
|
|
if (net->get_scalar() &&
|
2009-04-02 03:31:29 +02:00
|
|
|
word_sel != index_component_t::SEL_NONE) {
|
2009-04-15 01:08:27 +02:00
|
|
|
cerr << get_fileline() << ": error: can not select part of ";
|
|
|
|
|
if (res->expr_type() == IVL_VT_REAL) cerr << "real";
|
|
|
|
|
else cerr << "scalar";
|
|
|
|
|
cerr << " array word: " << net->name()
|
|
|
|
|
<<"[" << *word_index << "]" << endl;
|
2009-04-02 03:31:29 +02:00
|
|
|
des->errors += 1;
|
2009-04-02 20:05:09 +02:00
|
|
|
delete res;
|
2009-04-02 03:31:29 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
if (word_sel == index_component_t::SEL_PART)
|
2007-01-16 06:44:14 +01:00
|
|
|
return elaborate_expr_net_part_(des, scope, res, found_in);
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
if (word_sel == index_component_t::SEL_IDX_UP)
|
2007-01-16 06:44:14 +01:00
|
|
|
return elaborate_expr_net_idx_up_(des, scope, res, found_in);
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
if (word_sel == index_component_t::SEL_IDX_DO)
|
2007-01-16 06:44:14 +01:00
|
|
|
return elaborate_expr_net_idx_do_(des, scope, res, found_in);
|
|
|
|
|
|
2007-07-04 05:17:43 +02:00
|
|
|
if (word_sel == index_component_t::SEL_BIT)
|
|
|
|
|
return elaborate_expr_net_bit_(des, scope, res, found_in);
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
ivl_assert(*this, word_sel == index_component_t::SEL_NONE);
|
2009-07-04 00:37:44 +02:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2005-11-10 14:28:11 +01:00
|
|
|
/*
|
|
|
|
|
* Handle part selects of NetNet identifiers.
|
|
|
|
|
*/
|
|
|
|
|
NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope,
|
2007-01-16 06:44:14 +01:00
|
|
|
NetESignal*net, NetScope*found_in) const
|
2005-11-10 14:28:11 +01:00
|
|
|
{
|
2006-11-04 07:19:24 +01:00
|
|
|
long msv, lsv;
|
2009-01-02 01:20:41 +01:00
|
|
|
bool parts_defined_flag;
|
|
|
|
|
bool flag = calculate_parts_(des, scope, msv, lsv, parts_defined_flag);
|
2006-11-04 07:19:24 +01:00
|
|
|
if (!flag)
|
2005-11-10 14:28:11 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* The indices of part selects are signed integers, so allow
|
|
|
|
|
negative values. However, the width that they represent is
|
|
|
|
|
unsigned. Remember that any order is possible,
|
|
|
|
|
i.e., [1:0], [-4:6], etc. */
|
2008-06-07 07:05:17 +02:00
|
|
|
unsigned long wid = 1 + labs(msv-lsv);
|
2009-01-02 01:20:41 +01:00
|
|
|
/* But wait... if the part select expressions are not fully
|
|
|
|
|
defined, then fall back on the tested width. */
|
|
|
|
|
if (!parts_defined_flag) {
|
2010-01-10 04:57:01 +01:00
|
|
|
if (warn_ob_select) {
|
|
|
|
|
const index_component_t&psel = path_.back().index.back();
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
"Undefined part select [" << *(psel.msb) << ":"
|
|
|
|
|
<< *(psel.lsb) << "] for ";
|
|
|
|
|
if (net->word_index()) cerr << "array word";
|
|
|
|
|
else cerr << "vector";
|
|
|
|
|
cerr << " '" << net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "'." << endl;
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
"Replacing select with a constant 'bx." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetEConst*tmp = new NetEConst(verinum(verinum::Vx, 1, false));
|
2009-01-02 01:20:41 +01:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2010-01-10 04:57:01 +01:00
|
|
|
long sb_lsb = net->sig()->sb_to_idx(lsv);
|
|
|
|
|
long sb_msb = net->sig()->sb_to_idx(msv);
|
|
|
|
|
|
|
|
|
|
if (sb_msb < sb_lsb) {
|
|
|
|
|
cerr << get_fileline() << ": error: part select " << net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "[" << msv << ":" << lsv << "] is out of order." << endl;
|
2005-11-10 14:28:11 +01:00
|
|
|
des->errors += 1;
|
2006-11-04 07:19:24 +01:00
|
|
|
//delete lsn;
|
|
|
|
|
//delete msn;
|
2007-01-16 06:44:14 +01:00
|
|
|
return net;
|
2005-11-10 14:28:11 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-10 04:57:01 +01:00
|
|
|
if (warn_ob_select) {
|
|
|
|
|
if ((sb_lsb >= (signed) net->vector_width()) ||
|
|
|
|
|
(sb_msb >= (signed) net->vector_width())) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
"Part select " << "[" << msv << ":" << lsv
|
|
|
|
|
<< "] is selecting after the ";
|
|
|
|
|
if (net->word_index()) cerr << "array word ";
|
|
|
|
|
else cerr << "vector ";
|
|
|
|
|
cerr << net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "[" << net->msi() << ":" << net->lsi() << "]."
|
|
|
|
|
<< endl;
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
<< "Replacing the out of bound bits with 'bx." << endl;
|
|
|
|
|
}
|
|
|
|
|
if ((sb_msb < 0) || (sb_lsb < 0)) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
"Part select " << "[" << msv << ":" << lsv
|
|
|
|
|
<< "] is selecting before the ";
|
|
|
|
|
if (net->word_index()) cerr << "array word ";
|
|
|
|
|
else cerr << "vector ";
|
|
|
|
|
cerr << net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "[" << net->msi() << ":" << net->lsi() << "]."
|
|
|
|
|
<< endl;
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
"Replacing the out of bound bits with 'bx." << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2008-01-29 21:19:59 +01:00
|
|
|
// If the part select covers exactly the entire
|
2005-11-10 14:28:11 +01:00
|
|
|
// vector, then do not bother with it. Return the
|
2008-06-13 06:56:46 +02:00
|
|
|
// signal itself, casting to unsigned if necessary.
|
|
|
|
|
if (sb_lsb == 0 && wid == net->vector_width()) {
|
|
|
|
|
net->cast_signed(false);
|
2007-01-16 06:44:14 +01:00
|
|
|
return net;
|
2008-06-13 06:56:46 +02:00
|
|
|
}
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2008-06-07 07:05:17 +02:00
|
|
|
// If the part select covers NONE of the vector, then return a
|
|
|
|
|
// constant X.
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2008-06-07 07:05:17 +02:00
|
|
|
if ((sb_lsb >= (signed) net->vector_width()) || (sb_msb < 0)) {
|
|
|
|
|
NetEConst*tmp = make_const_x(wid);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-13 06:41:11 +02:00
|
|
|
NetExpr*ex = new NetEConst(verinum(sb_lsb));
|
|
|
|
|
NetESelect*ss = new NetESelect(net, ex, wid);
|
|
|
|
|
ss->set_line(*this);
|
|
|
|
|
return ss;
|
2005-11-10 14:28:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Part select indexed up, i.e. net[<m> +: <l>]
|
|
|
|
|
*/
|
|
|
|
|
NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope,
|
2007-01-16 06:44:14 +01:00
|
|
|
NetESignal*net, NetScope*found_in) const
|
2005-11-10 14:28:11 +01:00
|
|
|
{
|
2008-06-09 04:29:00 +02:00
|
|
|
NetExpr*base = calculate_up_do_base_(des, scope);
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2006-11-04 07:19:24 +01:00
|
|
|
unsigned long wid = 0;
|
|
|
|
|
calculate_up_do_width_(des, scope, wid);
|
2005-11-10 14:28:11 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// Handle the special case that the base is constant as
|
|
|
|
|
// well. In this case it can be converted to a conventional
|
|
|
|
|
// part select.
|
|
|
|
|
if (NetEConst*base_c = dynamic_cast<NetEConst*> (base)) {
|
2008-12-02 04:21:47 +01:00
|
|
|
NetExpr*ex;
|
|
|
|
|
if (base_c->value().is_defined()) {
|
|
|
|
|
long lsv = base_c->value().as_long();
|
|
|
|
|
|
|
|
|
|
// If the part select covers exactly the entire
|
|
|
|
|
// vector, then do not bother with it. Return the
|
|
|
|
|
// signal itself.
|
|
|
|
|
if (net->sig()->sb_to_idx(lsv) == 0 &&
|
|
|
|
|
wid == net->vector_width()) {
|
|
|
|
|
delete base;
|
2009-08-29 01:50:59 +02:00
|
|
|
net->cast_signed(false);
|
2008-12-02 04:21:47 +01:00
|
|
|
return net;
|
|
|
|
|
}
|
2007-04-01 07:28:26 +02:00
|
|
|
|
2009-08-29 01:50:59 +02:00
|
|
|
long offset = 0;
|
|
|
|
|
if (net->msi() < net->lsi()) {
|
|
|
|
|
offset = -wid + 1;
|
|
|
|
|
}
|
2008-12-02 04:21:47 +01:00
|
|
|
// Otherwise, make a part select that covers the right
|
|
|
|
|
// range.
|
2009-08-29 01:50:59 +02:00
|
|
|
ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv) +
|
|
|
|
|
offset));
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
long rel_base = net->sig()->sb_to_idx(lsv) + offset;
|
|
|
|
|
if (rel_base < 0) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "[" << lsv << "+:" << wid
|
|
|
|
|
<< "] is selecting before vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
if (rel_base + wid > net->vector_width()) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "[" << lsv << "+:" << wid
|
|
|
|
|
<< "] is selecting after vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-02 04:21:47 +01:00
|
|
|
} else {
|
|
|
|
|
// Return 'bx for an undefined base.
|
2009-08-29 01:50:59 +02:00
|
|
|
ex = new NetEConst(verinum(verinum::Vx, wid, true));
|
|
|
|
|
ex->set_line(*this);
|
|
|
|
|
delete base;
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
cerr << get_fileline() << ": warning: " << net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "['bx+:" << wid
|
|
|
|
|
<< "] is always outside vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
return ex;
|
2008-12-02 04:21:47 +01:00
|
|
|
}
|
2007-04-01 07:28:26 +02:00
|
|
|
NetESelect*ss = new NetESelect(net, ex, wid);
|
|
|
|
|
ss->set_line(*this);
|
|
|
|
|
|
|
|
|
|
delete base;
|
|
|
|
|
return ss;
|
2005-11-10 14:28:11 +01:00
|
|
|
}
|
|
|
|
|
|
2007-06-16 01:59:24 +02:00
|
|
|
if (net->msi() > net->lsi()) {
|
|
|
|
|
if (long offset = net->lsi())
|
2009-08-29 01:50:59 +02:00
|
|
|
base = make_add_expr(base, -offset);
|
2007-06-16 01:59:24 +02:00
|
|
|
} else {
|
2009-08-29 01:50:59 +02:00
|
|
|
base = make_sub_expr(net->lsi()-wid+1, base);
|
2007-06-16 01:59:24 +02:00
|
|
|
}
|
2007-04-01 07:28:26 +02:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
NetESelect*ss = new NetESelect(net, base, wid);
|
2005-11-10 14:28:11 +01:00
|
|
|
ss->set_line(*this);
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Elaborate part "
|
2005-11-10 14:28:11 +01:00
|
|
|
<< "select base="<< *base << ", wid="<< wid << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ss;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2007-07-06 21:46:32 +02:00
|
|
|
* Part select indexed down, i.e. net[<m> -: <l>]
|
2005-11-10 14:28:11 +01:00
|
|
|
*/
|
|
|
|
|
NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope,
|
2007-01-16 06:44:14 +01:00
|
|
|
NetESignal*net, NetScope*found_in)const
|
2005-11-10 14:28:11 +01:00
|
|
|
{
|
2008-12-02 04:21:47 +01:00
|
|
|
NetExpr*base = calculate_up_do_base_(des, scope);
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2006-11-04 07:19:24 +01:00
|
|
|
unsigned long wid = 0;
|
|
|
|
|
calculate_up_do_width_(des, scope, wid);
|
2005-11-10 14:28:11 +01:00
|
|
|
|
|
|
|
|
// Handle the special case that the base is constant as
|
|
|
|
|
// well. In this case it can be converted to a conventional
|
|
|
|
|
// part select.
|
|
|
|
|
if (NetEConst*base_c = dynamic_cast<NetEConst*> (base)) {
|
2008-12-02 04:21:47 +01:00
|
|
|
NetExpr*ex;
|
|
|
|
|
if (base_c->value().is_defined()) {
|
|
|
|
|
long lsv = base_c->value().as_long();
|
|
|
|
|
|
|
|
|
|
// If the part select covers exactly the entire
|
|
|
|
|
// vector, then do not bother with it. Return the
|
|
|
|
|
// signal itself.
|
|
|
|
|
if (net->sig()->sb_to_idx(lsv) == (signed) (wid-1) &&
|
|
|
|
|
wid == net->vector_width()) {
|
|
|
|
|
delete base;
|
2009-08-29 01:50:59 +02:00
|
|
|
net->cast_signed(false);
|
2008-12-02 04:21:47 +01:00
|
|
|
return net;
|
|
|
|
|
}
|
2007-04-01 07:28:26 +02:00
|
|
|
|
2009-08-29 01:50:59 +02:00
|
|
|
long offset = 0;
|
|
|
|
|
if (net->msi() > net->lsi()) {
|
|
|
|
|
offset = -wid + 1;
|
|
|
|
|
}
|
2008-12-02 04:21:47 +01:00
|
|
|
// Otherwise, make a part select that covers the right
|
|
|
|
|
// range.
|
2009-08-29 01:50:59 +02:00
|
|
|
ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv) +
|
|
|
|
|
offset));
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
long rel_base = net->sig()->sb_to_idx(lsv) + offset;
|
|
|
|
|
if (rel_base < 0) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "[" << lsv << "+:" << wid
|
|
|
|
|
<< "] is selecting before vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
if (rel_base + wid > net->vector_width()) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "[" << lsv << "-:" << wid
|
|
|
|
|
<< "] is selecting after vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-02 04:21:47 +01:00
|
|
|
} else {
|
|
|
|
|
// Return 'bx for an undefined base.
|
2009-08-29 01:50:59 +02:00
|
|
|
ex = new NetEConst(verinum(verinum::Vx, wid, true));
|
|
|
|
|
ex->set_line(*this);
|
|
|
|
|
delete base;
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
cerr << get_fileline() << ": warning: " << net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "['bx-:" << wid
|
|
|
|
|
<< "] is always outside vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
return ex;
|
2008-12-02 04:21:47 +01:00
|
|
|
}
|
2007-04-01 07:28:26 +02:00
|
|
|
NetESelect*ss = new NetESelect(net, ex, wid);
|
|
|
|
|
ss->set_line(*this);
|
|
|
|
|
|
|
|
|
|
delete base;
|
|
|
|
|
return ss;
|
2005-11-10 14:28:11 +01:00
|
|
|
}
|
|
|
|
|
|
2009-08-29 01:50:59 +02:00
|
|
|
if (net->msi() > net->lsi()) {
|
|
|
|
|
if (long offset = net->lsi()+wid-1)
|
|
|
|
|
base = make_add_expr(base, -offset);
|
|
|
|
|
} else {
|
|
|
|
|
base = make_sub_expr(net->lsi(), base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetESelect*ss = new NetESelect(net, base, wid);
|
2005-11-10 14:28:11 +01:00
|
|
|
ss->set_line(*this);
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Elaborate part "
|
2009-08-29 01:50:59 +02:00
|
|
|
<< "select base="<< *base << ", wid="<< wid << endl;
|
2005-11-10 14:28:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ss;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope,
|
2007-01-16 06:44:14 +01:00
|
|
|
NetESignal*net, NetScope*found_in) const
|
2005-11-10 14:28:11 +01:00
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
ivl_assert(*this, !name_tail.index.empty());
|
|
|
|
|
|
|
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
|
|
|
|
ivl_assert(*this, index_tail.msb != 0);
|
|
|
|
|
ivl_assert(*this, index_tail.lsb == 0);
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
NetExpr*ex = elab_and_eval(des, scope, index_tail.msb, -1);
|
2006-09-20 01:00:15 +02:00
|
|
|
|
2005-11-10 14:28:11 +01:00
|
|
|
// If the bit select is constant, then treat it similar
|
|
|
|
|
// to the part select, so that I save the effort of
|
|
|
|
|
// making a mux part in the netlist.
|
2006-09-20 01:00:15 +02:00
|
|
|
if (NetEConst*msc = dynamic_cast<NetEConst*> (ex)) {
|
2009-01-02 01:20:41 +01:00
|
|
|
|
2010-01-10 04:57:01 +01:00
|
|
|
// Special case: The bit select expression is constant
|
2009-01-02 01:20:41 +01:00
|
|
|
// x/z. The result of the expression is 1'bx.
|
|
|
|
|
if (! msc->value().is_defined()) {
|
2010-01-10 04:57:01 +01:00
|
|
|
if (warn_ob_select) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
"Constant bit select [" << msc->value()
|
|
|
|
|
<< "] is undefined for ";
|
|
|
|
|
if (net->word_index()) cerr << "array word";
|
|
|
|
|
else cerr << "vector";
|
|
|
|
|
cerr << " '" << net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "'." << endl;
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
<< "Replacing select with a constant 1'bx."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-02 01:20:41 +01:00
|
|
|
NetEConst*tmp = make_const_x(1);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
delete ex;
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-20 01:00:15 +02:00
|
|
|
long msv = msc->value().as_long();
|
2010-01-10 04:57:01 +01:00
|
|
|
long idx = net->sig()->sb_to_idx(msv);
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2010-01-10 04:57:01 +01:00
|
|
|
if (idx >= (long)net->vector_width() || idx < 0) {
|
2005-11-10 14:28:11 +01:00
|
|
|
/* The bit select is out of range of the
|
|
|
|
|
vector. This is legal, but returns a
|
|
|
|
|
constant 1'bx value. */
|
2010-01-10 04:57:01 +01:00
|
|
|
if (warn_ob_select) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
"Constant bit select [" << msv
|
|
|
|
|
<< "] is ";
|
|
|
|
|
if (idx < 0) cerr << "before ";
|
|
|
|
|
else cerr << "after ";
|
|
|
|
|
if (net->word_index()) cerr << "array word ";
|
|
|
|
|
else cerr << "vector ";
|
|
|
|
|
cerr << net->name();
|
|
|
|
|
if (net->word_index()) cerr << "[]";
|
|
|
|
|
cerr << "[" << net->sig()->msb() << ":"
|
|
|
|
|
<< net->sig()->lsb() << "]." << endl;
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
<< "Replacing select with a constant 1'bx."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-07 07:05:17 +02:00
|
|
|
NetEConst*tmp = make_const_x(1);
|
2005-11-10 14:28:11 +01:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
|
2006-09-20 01:00:15 +02:00
|
|
|
delete ex;
|
2005-11-10 14:28:11 +01:00
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the vector is only one bit, we are done. The
|
2008-01-29 21:19:59 +01:00
|
|
|
// bit select will return the scalar itself.
|
2005-11-10 14:28:11 +01:00
|
|
|
if (net->vector_width() == 1)
|
2007-01-16 06:44:14 +01:00
|
|
|
return net;
|
2005-11-10 14:28:11 +01:00
|
|
|
|
|
|
|
|
// Make an expression out of the index
|
|
|
|
|
NetEConst*idx_c = new NetEConst(verinum(idx));
|
|
|
|
|
idx_c->set_line(*net);
|
|
|
|
|
|
|
|
|
|
// Make a bit select with the canonical index
|
2007-01-16 06:44:14 +01:00
|
|
|
NetESelect*res = new NetESelect(net, idx_c, 1);
|
2005-11-10 14:28:11 +01:00
|
|
|
res->set_line(*net);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Non-constant bit select? punt and make a subsignal
|
|
|
|
|
// device to mux the bit in the net. This is a fairly
|
|
|
|
|
// complicated task because we need to generate
|
|
|
|
|
// expressions to convert calculated bit select
|
|
|
|
|
// values to canonical values that are used internally.
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
if (net->sig()->msb() < net->sig()->lsb()) {
|
|
|
|
|
ex = make_sub_expr(net->sig()->lsb(), ex);
|
2005-11-10 14:28:11 +01:00
|
|
|
} else {
|
2007-01-16 06:44:14 +01:00
|
|
|
ex = make_add_expr(ex, - net->sig()->lsb());
|
2005-11-10 14:28:11 +01:00
|
|
|
}
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
NetESelect*ss = new NetESelect(net, ex, 1);
|
2005-11-10 14:28:11 +01:00
|
|
|
ss->set_line(*this);
|
|
|
|
|
return ss;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope,
|
2007-01-16 06:44:14 +01:00
|
|
|
NetNet*net, NetScope*found_in,
|
|
|
|
|
bool sys_task_arg) const
|
2005-11-10 14:28:11 +01:00
|
|
|
{
|
2007-01-16 06:44:14 +01:00
|
|
|
if (net->array_dimensions() > 0)
|
|
|
|
|
return elaborate_expr_net_word_(des, scope, net, found_in, sys_task_arg);
|
|
|
|
|
|
|
|
|
|
NetESignal*node = new NetESignal(net);
|
|
|
|
|
node->set_line(*this);
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
|
|
|
|
|
if (! path_.back().index.empty())
|
|
|
|
|
use_sel = path_.back().index.back().sel;
|
|
|
|
|
|
2009-04-15 01:08:27 +02:00
|
|
|
if (net->get_scalar() &&
|
2009-04-02 03:31:29 +02:00
|
|
|
use_sel != index_component_t::SEL_NONE) {
|
2009-04-15 01:08:27 +02:00
|
|
|
cerr << get_fileline() << ": error: can not select part of ";
|
|
|
|
|
if (node->expr_type() == IVL_VT_REAL) cerr << "real: ";
|
|
|
|
|
else cerr << "scalar: ";
|
|
|
|
|
cerr << net->name() << endl;
|
2009-04-02 03:31:29 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-11-10 14:28:11 +01:00
|
|
|
// If this is a part select of a signal, then make a new
|
|
|
|
|
// temporary signal that is connected to just the
|
|
|
|
|
// selected bits. The lsb_ and msb_ expressions are from
|
|
|
|
|
// the foo[msb:lsb] expression in the original.
|
2007-05-24 06:07:11 +02:00
|
|
|
if (use_sel == index_component_t::SEL_PART)
|
2007-01-16 06:44:14 +01:00
|
|
|
return elaborate_expr_net_part_(des, scope, node, found_in);
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
if (use_sel == index_component_t::SEL_IDX_UP)
|
2007-01-16 06:44:14 +01:00
|
|
|
return elaborate_expr_net_idx_up_(des, scope, node, found_in);
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
if (use_sel == index_component_t::SEL_IDX_DO)
|
2007-01-16 06:44:14 +01:00
|
|
|
return elaborate_expr_net_idx_do_(des, scope, node, found_in);
|
2005-11-10 14:28:11 +01:00
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
if (use_sel == index_component_t::SEL_BIT)
|
2007-01-16 06:44:14 +01:00
|
|
|
return elaborate_expr_net_bit_(des, scope, node, found_in);
|
2006-02-02 03:43:57 +01:00
|
|
|
|
2005-11-10 14:28:11 +01:00
|
|
|
// It's not anything else, so this must be a simple identifier
|
|
|
|
|
// expression with no part or bit select. Return the signal
|
|
|
|
|
// itself as the expression.
|
2007-05-24 06:07:11 +02:00
|
|
|
assert(use_sel == index_component_t::SEL_NONE);
|
2005-11-10 14:28:11 +01:00
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-04 07:19:24 +01:00
|
|
|
unsigned PENumber::test_width(Design*, NetScope*,
|
2008-09-27 07:20:11 +02:00
|
|
|
unsigned min, unsigned lval,
|
2009-01-06 04:44:52 +01:00
|
|
|
ivl_variable_type_t&use_expr_type,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&unsized_flag)
|
2006-10-30 06:44:49 +01:00
|
|
|
{
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = IVL_VT_LOGIC;
|
2006-10-30 06:44:49 +01:00
|
|
|
unsigned use_wid = value_->len();
|
|
|
|
|
if (min > use_wid)
|
|
|
|
|
use_wid = min;
|
|
|
|
|
|
|
|
|
|
if (! value_->has_len())
|
|
|
|
|
unsized_flag = true;
|
|
|
|
|
|
2008-09-22 03:14:26 +02:00
|
|
|
if (lval > 0 && lval < use_wid)
|
|
|
|
|
use_wid = lval;
|
|
|
|
|
|
2009-01-06 04:44:52 +01:00
|
|
|
use_expr_type = expr_type_;
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_width_ = use_wid;
|
2006-10-30 06:44:49 +01:00
|
|
|
return use_wid;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetEConst* PENumber::elaborate_expr(Design*des, NetScope*,
|
2008-10-20 19:06:04 +02:00
|
|
|
int expr_width__, bool) const
|
2000-03-08 05:36:53 +01:00
|
|
|
{
|
|
|
|
|
assert(value_);
|
2006-07-07 06:06:37 +02:00
|
|
|
verinum tvalue = *value_;
|
|
|
|
|
|
|
|
|
|
// If the expr_width is >0, then the context is requesting a
|
|
|
|
|
// specific size (for example this is part of the r-values of
|
|
|
|
|
// an assignment) so we pad to the desired width and ignore
|
|
|
|
|
// the self-determined size.
|
2008-10-20 19:06:04 +02:00
|
|
|
if (expr_width__ > 0) {
|
|
|
|
|
tvalue = pad_to_width(tvalue, expr_width__);
|
|
|
|
|
if (tvalue.len() > (unsigned)expr_width__) {
|
|
|
|
|
verinum tmp (tvalue, expr_width__);
|
2008-09-22 03:14:26 +02:00
|
|
|
tmp.has_sign(tvalue.has_sign());
|
|
|
|
|
tvalue = tmp;
|
|
|
|
|
}
|
2006-07-07 06:06:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetEConst*tmp = new NetEConst(tvalue);
|
2000-03-08 05:36:53 +01:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-10 05:54:26 +01:00
|
|
|
unsigned PEString::test_width(Design*des, NetScope*scope,
|
|
|
|
|
unsigned min, unsigned lval,
|
2008-10-20 19:06:04 +02:00
|
|
|
ivl_variable_type_t&expr_type__,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&unsized_flag)
|
2006-11-10 05:54:26 +01:00
|
|
|
{
|
2008-10-19 07:00:22 +02:00
|
|
|
expr_type_ = IVL_VT_BOOL;
|
|
|
|
|
expr_width_ = text_? 8*strlen(text_) : 0;
|
|
|
|
|
if (min > expr_width_)
|
|
|
|
|
expr_width_ = min;
|
2006-11-10 05:54:26 +01:00
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2008-10-19 07:00:22 +02:00
|
|
|
return expr_width_;
|
2006-11-10 05:54:26 +01:00
|
|
|
}
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetEConst* PEString::elaborate_expr(Design*des, NetScope*,
|
2008-10-20 19:06:04 +02:00
|
|
|
int expr_width_dummy, bool) const
|
2000-03-08 05:36:53 +01:00
|
|
|
{
|
|
|
|
|
NetEConst*tmp = new NetEConst(value());
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-10 05:54:26 +01:00
|
|
|
unsigned PETernary::test_width(Design*des, NetScope*scope,
|
|
|
|
|
unsigned min, unsigned lval,
|
2009-01-06 04:44:52 +01:00
|
|
|
ivl_variable_type_t&use_expr_type,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&flag)
|
2006-11-10 05:54:26 +01:00
|
|
|
{
|
2008-10-19 07:00:22 +02:00
|
|
|
// The condition of the ternary is self-determined, but we
|
|
|
|
|
// test its width to force its type to be calculated.
|
|
|
|
|
ivl_variable_type_t con_type = IVL_VT_NO_TYPE;
|
|
|
|
|
bool con_flag = false;
|
|
|
|
|
expr_->test_width(des, scope, 0, 0, con_type, con_flag);
|
|
|
|
|
|
2008-09-27 07:20:11 +02:00
|
|
|
ivl_variable_type_t tru_type = IVL_VT_NO_TYPE;
|
|
|
|
|
unsigned tru_wid = tru_->test_width(des, scope, min, lval, tru_type,flag);
|
|
|
|
|
|
2008-09-21 03:17:17 +02:00
|
|
|
bool initial_flag = flag;
|
2008-09-27 07:20:11 +02:00
|
|
|
ivl_variable_type_t fal_type = IVL_VT_NO_TYPE;
|
|
|
|
|
unsigned fal_wid = fal_->test_width(des, scope, min, lval, fal_type,flag);
|
2008-09-21 03:17:17 +02:00
|
|
|
|
|
|
|
|
// If the false clause is unsized, then try again with the
|
|
|
|
|
// true clause, because it might choose a different width if
|
|
|
|
|
// it is in an unsized context.
|
|
|
|
|
if (initial_flag == false && flag == true) {
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "False clause is unsized, so retest width of true clause."
|
|
|
|
|
<< endl;
|
2008-09-27 07:20:11 +02:00
|
|
|
tru_wid = tru_->test_width(des, scope, max(min,fal_wid), lval, tru_type, flag);
|
2008-09-21 03:17:17 +02:00
|
|
|
}
|
|
|
|
|
|
2009-01-06 04:44:52 +01:00
|
|
|
// If either of the alternatives is IVL_VT_REAL, then the
|
|
|
|
|
// expression as a whole is IVL_VT_REAL. Otherwise, if either
|
|
|
|
|
// of the alternatives is IVL_VT_LOGIC, then the expression as
|
|
|
|
|
// a whole is IVL_VT_LOGIC. The fallback assumes that the
|
|
|
|
|
// types are the same and we take that.
|
|
|
|
|
if (tru_type == IVL_VT_REAL || fal_type == IVL_VT_REAL) {
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = IVL_VT_REAL;
|
2009-01-06 04:44:52 +01:00
|
|
|
expr_width_ = 1;
|
|
|
|
|
} else if (tru_type == IVL_VT_LOGIC || fal_type == IVL_VT_LOGIC) {
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = IVL_VT_LOGIC;
|
2009-01-06 04:44:52 +01:00
|
|
|
expr_width_ = max(tru_wid,fal_wid);
|
|
|
|
|
} else {
|
|
|
|
|
ivl_assert(*this, tru_type == fal_type);
|
2008-10-11 05:42:07 +02:00
|
|
|
expr_type_ = tru_type;
|
2009-01-06 04:44:52 +01:00
|
|
|
expr_width_ = max(tru_wid,fal_wid);
|
|
|
|
|
}
|
2008-10-11 05:42:07 +02:00
|
|
|
|
2009-01-06 04:44:52 +01:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: "
|
|
|
|
|
<< "Ternary expression type=" << expr_type_
|
|
|
|
|
<< ", width=" << expr_width_
|
|
|
|
|
<< ", unsized_flag=" << flag
|
|
|
|
|
<< " (tru_type=" << tru_type
|
|
|
|
|
<< ", fal_type=" << fal_type << ")" << endl;
|
2008-10-11 05:42:07 +02:00
|
|
|
|
2009-01-06 04:44:52 +01:00
|
|
|
use_expr_type = expr_type_;
|
2008-10-11 05:42:07 +02:00
|
|
|
return expr_width_;
|
2006-11-10 05:54:26 +01:00
|
|
|
}
|
|
|
|
|
|
2008-09-14 01:43:39 +02:00
|
|
|
bool NetETernary::test_operand_compat(ivl_variable_type_t l,
|
|
|
|
|
ivl_variable_type_t r)
|
2005-09-14 04:53:13 +02:00
|
|
|
{
|
|
|
|
|
if (l == IVL_VT_LOGIC && r == IVL_VT_BOOL)
|
|
|
|
|
return true;
|
|
|
|
|
if (l == IVL_VT_BOOL && r == IVL_VT_LOGIC)
|
|
|
|
|
return true;
|
2008-03-08 03:51:50 +01:00
|
|
|
|
|
|
|
|
if (l == IVL_VT_REAL && (r == IVL_VT_LOGIC || r == IVL_VT_BOOL))
|
|
|
|
|
return true;
|
|
|
|
|
if (r == IVL_VT_REAL && (l == IVL_VT_LOGIC || l == IVL_VT_BOOL))
|
|
|
|
|
return true;
|
|
|
|
|
|
2005-09-14 04:53:13 +02:00
|
|
|
if (l == r)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
1999-09-30 02:48:49 +02:00
|
|
|
/*
|
|
|
|
|
* Elaborate the Ternary operator. I know that the expressions were
|
|
|
|
|
* parsed so I can presume that they exist, and call elaboration
|
|
|
|
|
* methods. If any elaboration fails, then give up and return 0.
|
|
|
|
|
*/
|
2008-08-23 19:50:24 +02:00
|
|
|
NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope,
|
2006-06-02 06:48:49 +02:00
|
|
|
int expr_wid, bool) const
|
1999-09-30 02:48:49 +02:00
|
|
|
{
|
|
|
|
|
assert(expr_);
|
|
|
|
|
assert(tru_);
|
|
|
|
|
assert(fal_);
|
|
|
|
|
|
2008-09-21 03:17:17 +02:00
|
|
|
int use_wid = expr_wid >= 0? expr_wid : 0;
|
|
|
|
|
|
2006-11-10 05:54:26 +01:00
|
|
|
if (expr_wid < 0) {
|
2008-10-11 05:42:07 +02:00
|
|
|
use_wid = expr_width();
|
2006-11-10 05:54:26 +01:00
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: "
|
2008-09-21 03:17:17 +02:00
|
|
|
<< "Self-sized ternary chooses wid="<< use_wid
|
2008-10-11 05:42:07 +02:00
|
|
|
<< ", type=" << expr_type()
|
2008-10-19 07:00:22 +02:00
|
|
|
<< ", expr=" << *this
|
2008-09-21 03:17:17 +02:00
|
|
|
<< endl;
|
2008-10-19 07:00:22 +02:00
|
|
|
|
2008-09-21 03:17:17 +02:00
|
|
|
ivl_assert(*this, use_wid > 0);
|
2006-11-10 05:54:26 +01:00
|
|
|
}
|
|
|
|
|
|
2008-08-23 19:50:24 +02:00
|
|
|
// Elaborate and evaluate the condition expression. Note that
|
|
|
|
|
// it is always self-determined.
|
|
|
|
|
NetExpr*con = elab_and_eval(des, scope, expr_, -1);
|
1999-09-30 02:48:49 +02:00
|
|
|
if (con == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2008-08-23 19:50:24 +02:00
|
|
|
/* Make sure the condition expression reduces to a single bit. */
|
|
|
|
|
con = condition_reduce(con);
|
|
|
|
|
|
|
|
|
|
// Verilog doesn't say that we must do short circuit
|
|
|
|
|
// evaluation of ternary expressions, but it doesn't disallow
|
|
|
|
|
// it. The disadvantage of doing this is that semantic errors
|
|
|
|
|
// in the unused clause will be missed, but people don't seem
|
|
|
|
|
// to mind, and do apreciate the optimization available here.
|
|
|
|
|
if (NetEConst*tmp = dynamic_cast<NetEConst*> (con)) {
|
|
|
|
|
verinum cval = tmp->value();
|
|
|
|
|
ivl_assert(*this, cval.len()==1);
|
|
|
|
|
|
|
|
|
|
// Condition is constant TRUE, so we only need the true claue.
|
|
|
|
|
if (cval.get(0) == verinum::V1) {
|
2008-09-18 18:30:44 +02:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: Short-circuit "
|
|
|
|
|
"elaborate TRUE clause of ternary."
|
|
|
|
|
<< endl;
|
2008-11-19 01:52:05 +01:00
|
|
|
if (use_wid <= 0) {
|
|
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "Unexpected use_wid=" << use_wid
|
|
|
|
|
<< " processing short-circuit TRUE clause"
|
|
|
|
|
<< " of expression: " << *this << endl;
|
|
|
|
|
}
|
2008-09-28 17:57:25 +02:00
|
|
|
ivl_assert(*this, use_wid > 0);
|
2009-01-06 04:44:52 +01:00
|
|
|
NetExpr*tmp = elab_and_eval_alternative_(des, scope, tru_, use_wid);
|
|
|
|
|
return pad_to_width(tmp, use_wid, *this);
|
2008-08-23 19:50:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Condition is constant FALSE, so we only need the
|
|
|
|
|
// false clause.
|
|
|
|
|
if (cval.get(0) == verinum::V0) {
|
2008-09-18 18:30:44 +02:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_fileline() << ": debug: Short-circuit "
|
|
|
|
|
"elaborate FALSE clause of ternary."
|
|
|
|
|
<< endl;
|
2008-11-19 01:52:05 +01:00
|
|
|
if (use_wid <= 0) {
|
|
|
|
|
cerr << get_fileline() << ": internal error: "
|
|
|
|
|
<< "Unexpected use_wid=" << use_wid
|
|
|
|
|
<< " processing short-circuit FALSE clause"
|
|
|
|
|
<< " of expression: " << *this << endl;
|
|
|
|
|
}
|
2008-09-28 17:57:25 +02:00
|
|
|
ivl_assert(*this, use_wid > 0);
|
2009-01-06 04:44:52 +01:00
|
|
|
NetExpr*tmp = elab_and_eval_alternative_(des, scope, fal_, use_wid);
|
|
|
|
|
return pad_to_width(tmp, use_wid, *this);
|
2008-08-23 19:50:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// X and Z conditions need to blend both results, so we
|
|
|
|
|
// can't short-circuit.
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 04:44:52 +01:00
|
|
|
NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, use_wid);
|
1999-09-30 02:48:49 +02:00
|
|
|
if (tru == 0) {
|
|
|
|
|
delete con;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 04:44:52 +01:00
|
|
|
NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, use_wid);
|
1999-09-30 02:48:49 +02:00
|
|
|
if (fal == 0) {
|
|
|
|
|
delete con;
|
|
|
|
|
delete tru;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-09-14 01:43:39 +02:00
|
|
|
if (! NetETernary::test_operand_compat(tru->expr_type(), fal->expr_type())) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Data types "
|
2005-09-01 06:10:47 +02:00
|
|
|
<< tru->expr_type() << " and "
|
|
|
|
|
<< fal->expr_type() << " of ternary"
|
|
|
|
|
<< " do not match." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-19 06:33:31 +01:00
|
|
|
suppress_binary_operand_sign_if_needed(tru, fal);
|
2008-10-12 06:52:41 +02:00
|
|
|
|
2006-11-10 05:54:26 +01:00
|
|
|
/* Whatever the width we choose for the ternary operator, we
|
|
|
|
|
need to make sure the operands match. */
|
2008-11-19 02:17:19 +01:00
|
|
|
tru = pad_to_width(tru, use_wid, *this);
|
|
|
|
|
fal = pad_to_width(fal, use_wid, *this);
|
2006-11-10 05:54:26 +01:00
|
|
|
|
1999-09-30 02:48:49 +02:00
|
|
|
NetETernary*res = new NetETernary(con, tru, fal);
|
2009-12-17 03:14:07 +01:00
|
|
|
res->cast_signed(tru->has_sign() && fal->has_sign());
|
2003-04-19 06:19:38 +02:00
|
|
|
res->set_line(*this);
|
1999-09-30 02:48:49 +02:00
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 04:44:52 +01:00
|
|
|
/*
|
|
|
|
|
* When elaborating the true or false alternative expression of a
|
|
|
|
|
* ternary, take into account the overall expression type. If the type
|
|
|
|
|
* is not vectorable, then the alternative expression is evaluated as
|
|
|
|
|
* self-determined.
|
|
|
|
|
*/
|
|
|
|
|
NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope,
|
|
|
|
|
PExpr*expr, int use_wid) const
|
|
|
|
|
{
|
|
|
|
|
if (type_is_vectorable(expr->expr_type()) && !type_is_vectorable(expr_type_)) {
|
|
|
|
|
return elab_and_eval(des, scope, expr, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return elab_and_eval(des, scope, expr, use_wid);
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-16 07:03:19 +02:00
|
|
|
unsigned PEUnary::test_width(Design*des, NetScope*scope,
|
2008-09-27 07:20:11 +02:00
|
|
|
unsigned min, unsigned lval,
|
2008-10-20 19:06:04 +02:00
|
|
|
ivl_variable_type_t&expr_type__,
|
2008-10-11 05:42:07 +02:00
|
|
|
bool&unsized_flag)
|
2008-08-16 07:03:19 +02:00
|
|
|
{
|
|
|
|
|
switch (op_) {
|
2008-10-21 18:00:48 +02:00
|
|
|
case '&': // Reduction AND
|
2008-08-16 07:03:19 +02:00
|
|
|
case '|': // Reduction OR
|
|
|
|
|
case '^': // Reduction XOR
|
|
|
|
|
case 'A': // Reduction NAND (~&)
|
|
|
|
|
case 'N': // Reduction NOR (~|)
|
|
|
|
|
case 'X': // Reduction NXOR (~^)
|
2008-10-19 07:00:22 +02:00
|
|
|
{
|
|
|
|
|
ivl_variable_type_t sub_type = IVL_VT_NO_TYPE;
|
|
|
|
|
bool flag = false;
|
|
|
|
|
expr_->test_width(des, scope, 0, 0, sub_type, flag);
|
|
|
|
|
expr_type_ = sub_type;
|
|
|
|
|
}
|
|
|
|
|
expr_width_ = 1;
|
|
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type__ = expr_type_;
|
2009-02-19 01:02:58 +01:00
|
|
|
return expr_width_;
|
|
|
|
|
|
|
|
|
|
case '!':
|
|
|
|
|
{
|
|
|
|
|
ivl_variable_type_t sub_type = IVL_VT_NO_TYPE;
|
|
|
|
|
bool flag = false;
|
|
|
|
|
expr_->test_width(des, scope, 0, 0, sub_type, flag);
|
|
|
|
|
expr_type_ = (sub_type==IVL_VT_BOOL)? IVL_VT_BOOL : IVL_VT_LOGIC;
|
|
|
|
|
}
|
|
|
|
|
// Logical ! always returns a single-bit LOGIC or BOOL value.
|
|
|
|
|
expr_width_ = 1;
|
|
|
|
|
expr_type__ = expr_type_;
|
2008-10-19 07:00:22 +02:00
|
|
|
return expr_width_;
|
2008-08-16 07:03:19 +02:00
|
|
|
}
|
2008-08-27 06:33:24 +02:00
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
unsigned test_wid = expr_->test_width(des, scope, min, lval, expr_type__, unsized_flag);
|
2008-08-27 06:33:24 +02:00
|
|
|
switch (op_) {
|
|
|
|
|
// For these operators, the act of padding to the
|
|
|
|
|
// minimum width can have an important impact on the
|
|
|
|
|
// calculation. So don't let the tested width be less
|
2010-04-12 03:39:56 +02:00
|
|
|
// then the minimum width.
|
2008-08-27 06:33:24 +02:00
|
|
|
case '-':
|
|
|
|
|
case '+':
|
2008-10-13 06:38:07 +02:00
|
|
|
case '~':
|
2008-08-27 06:33:24 +02:00
|
|
|
if (test_wid < min)
|
|
|
|
|
test_wid = min;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
expr_type_ = expr_type__;
|
2008-10-19 07:00:22 +02:00
|
|
|
expr_width_ = test_wid;
|
2008-08-27 06:33:24 +02:00
|
|
|
return test_wid;
|
2008-08-16 07:03:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope,
|
|
|
|
|
int expr_wid, bool) const
|
2000-03-08 05:36:53 +01:00
|
|
|
{
|
2008-03-26 03:00:00 +01:00
|
|
|
/* Reduction operators and ! always have a self determined width. */
|
|
|
|
|
switch (op_) {
|
|
|
|
|
case '!':
|
|
|
|
|
case '&': // Reduction AND
|
|
|
|
|
case '|': // Reduction OR
|
|
|
|
|
case '^': // Reduction XOR
|
|
|
|
|
case 'A': // Reduction NAND (~&)
|
|
|
|
|
case 'N': // Reduction NOR (~|)
|
|
|
|
|
case 'X': // Reduction NXOR (~^)
|
|
|
|
|
expr_wid = -1;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*ip = expr_->elaborate_expr(des, scope, expr_wid, false);
|
2000-03-08 05:36:53 +01:00
|
|
|
if (ip == 0) return 0;
|
|
|
|
|
|
2008-11-27 00:37:38 +01:00
|
|
|
ivl_assert(*expr_, expr_type_ != IVL_VT_NO_TYPE);
|
|
|
|
|
|
2002-04-14 05:55:25 +02:00
|
|
|
NetExpr*tmp;
|
2000-03-08 05:36:53 +01:00
|
|
|
switch (op_) {
|
|
|
|
|
default:
|
|
|
|
|
tmp = new NetEUnary(op_, ip);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
break;
|
2002-04-14 05:55:25 +02:00
|
|
|
|
|
|
|
|
case '-':
|
|
|
|
|
if (NetEConst*ipc = dynamic_cast<NetEConst*>(ip)) {
|
2006-06-02 06:48:49 +02:00
|
|
|
|
|
|
|
|
verinum val = ipc->value();
|
|
|
|
|
if (expr_wid > 0)
|
|
|
|
|
val = pad_to_width(val, expr_wid);
|
|
|
|
|
|
2008-05-09 23:18:39 +02:00
|
|
|
/* When taking the - of a number, extend it one
|
2008-09-22 03:14:26 +02:00
|
|
|
bit to accommodate a possible sign bit.
|
|
|
|
|
|
|
|
|
|
NOTE: This may not be correct! The test_width
|
|
|
|
|
is supposed to detect the special case that we
|
|
|
|
|
want to do lossless self-determined
|
|
|
|
|
expressions, and the function that calls
|
|
|
|
|
elaborate_expr should account for that in the
|
|
|
|
|
expr_wid argument. */
|
|
|
|
|
unsigned use_len = val.len();
|
|
|
|
|
if (expr_wid < 0)
|
|
|
|
|
use_len += 1;
|
|
|
|
|
|
|
|
|
|
/* Calculate unary minus as 0-val */
|
|
|
|
|
verinum zero (verinum::V0, use_len, val.has_len());
|
2008-01-22 07:05:50 +01:00
|
|
|
zero.has_sign(val.has_sign());
|
2006-06-02 06:48:49 +02:00
|
|
|
verinum nval = zero - val;
|
|
|
|
|
|
|
|
|
|
if (val.has_len())
|
|
|
|
|
nval = verinum(nval, val.len());
|
2008-05-09 23:18:39 +02:00
|
|
|
nval.has_sign(val.has_sign());
|
2006-06-02 06:48:49 +02:00
|
|
|
tmp = new NetEConst(nval);
|
|
|
|
|
tmp->set_line(*this);
|
2002-04-14 05:55:25 +02:00
|
|
|
delete ip;
|
2004-06-05 01:34:15 +02:00
|
|
|
|
2008-10-20 19:06:04 +02:00
|
|
|
} else if (NetECReal*ipr = dynamic_cast<NetECReal*>(ip)) {
|
2004-06-05 01:34:15 +02:00
|
|
|
|
|
|
|
|
/* When taking the - of a real, fold this into the
|
|
|
|
|
constant value. */
|
2008-10-20 19:06:04 +02:00
|
|
|
verireal val = - ipr->value();
|
2004-06-05 01:34:15 +02:00
|
|
|
tmp = new NetECReal(val);
|
2009-02-14 03:25:54 +01:00
|
|
|
tmp->set_line(*this);
|
2004-06-05 01:34:15 +02:00
|
|
|
delete ip;
|
|
|
|
|
|
2002-04-14 05:55:25 +02:00
|
|
|
} else {
|
2008-08-17 02:30:32 +02:00
|
|
|
if (expr_wid > 0)
|
2008-11-19 02:17:19 +01:00
|
|
|
ip = pad_to_width(ip, expr_wid, *this);
|
2002-04-14 05:55:25 +02:00
|
|
|
tmp = new NetEUnary(op_, ip);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '+':
|
|
|
|
|
tmp = ip;
|
|
|
|
|
break;
|
|
|
|
|
|
2000-11-29 06:24:00 +01:00
|
|
|
case '!': // Logical NOT
|
2002-04-14 23:16:48 +02:00
|
|
|
/* If the operand to unary ! is a constant, then I can
|
|
|
|
|
evaluate this expression here and return a logical
|
|
|
|
|
constant in its place. */
|
|
|
|
|
if (NetEConst*ipc = dynamic_cast<NetEConst*>(ip)) {
|
|
|
|
|
verinum val = ipc->value();
|
|
|
|
|
unsigned v1 = 0;
|
|
|
|
|
unsigned vx = 0;
|
|
|
|
|
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
|
|
|
|
|
switch (val[idx]) {
|
|
|
|
|
case verinum::V0:
|
|
|
|
|
break;
|
|
|
|
|
case verinum::V1:
|
|
|
|
|
v1 += 1;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
vx += 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verinum::V res;
|
|
|
|
|
if (v1 > 0)
|
|
|
|
|
res = verinum::V0;
|
|
|
|
|
else if (vx > 0)
|
|
|
|
|
res = verinum::Vx;
|
|
|
|
|
else
|
|
|
|
|
res = verinum::V1;
|
|
|
|
|
|
2009-02-14 03:25:54 +01:00
|
|
|
verinum vres (res, 1, true);
|
|
|
|
|
tmp = new NetEConst(vres);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
delete ip;
|
|
|
|
|
} else if (NetECReal*ipr = dynamic_cast<NetECReal*>(ip)) {
|
|
|
|
|
verinum::V res;
|
|
|
|
|
if (ipr->value().as_double() == 0.0) res = verinum::V1;
|
|
|
|
|
else res = verinum::V0;
|
2002-04-14 23:16:48 +02:00
|
|
|
verinum vres (res, 1, true);
|
|
|
|
|
tmp = new NetEConst(vres);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
delete ip;
|
|
|
|
|
} else {
|
2009-02-14 03:25:54 +01:00
|
|
|
if (ip->expr_type() == IVL_VT_REAL) {
|
|
|
|
|
tmp = new NetEBComp('e', ip,
|
|
|
|
|
new NetECReal(verireal(0.0)));
|
|
|
|
|
} else {
|
|
|
|
|
tmp = new NetEUReduce(op_, ip);
|
|
|
|
|
}
|
2002-04-14 23:16:48 +02:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2000-11-29 06:24:00 +01:00
|
|
|
case '&': // Reduction AND
|
2001-01-02 05:21:13 +01:00
|
|
|
case '|': // Reduction OR
|
2000-11-29 06:24:00 +01:00
|
|
|
case '^': // Reduction XOR
|
|
|
|
|
case 'A': // Reduction NAND (~&)
|
|
|
|
|
case 'N': // Reduction NOR (~|)
|
|
|
|
|
case 'X': // Reduction NXOR (~^)
|
2009-02-14 03:25:54 +01:00
|
|
|
if (ip->expr_type() == IVL_VT_REAL) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< human_readable_op(op_, true)
|
|
|
|
|
<< " operator may not have a REAL operand." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2000-11-29 06:24:00 +01:00
|
|
|
tmp = new NetEUReduce(op_, ip);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
break;
|
2002-04-14 05:55:25 +02:00
|
|
|
|
2000-03-08 05:36:53 +01:00
|
|
|
case '~':
|
2008-10-13 06:38:07 +02:00
|
|
|
tmp = elaborate_expr_bits_(ip, expr_wid);
|
2000-03-08 05:36:53 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2001-11-19 03:54:12 +01:00
|
|
|
|
2000-03-08 05:36:53 +01:00
|
|
|
return tmp;
|
|
|
|
|
}
|
2008-10-13 06:38:07 +02:00
|
|
|
|
|
|
|
|
NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, int expr_wid) const
|
|
|
|
|
{
|
|
|
|
|
// Handle the special case that the operand is a
|
|
|
|
|
// constant. Simply calculate the constant results of the
|
|
|
|
|
// expression and return that.
|
|
|
|
|
if (NetEConst*ctmp = dynamic_cast<NetEConst*> (operand)) {
|
|
|
|
|
verinum value = ctmp->value();
|
|
|
|
|
if (expr_wid > (int)value.len())
|
|
|
|
|
value = pad_to_width(value, expr_wid);
|
|
|
|
|
|
|
|
|
|
// The only operand that I know can get here is the
|
|
|
|
|
// unary not (~).
|
|
|
|
|
ivl_assert(*this, op_ == '~');
|
|
|
|
|
value = v_not(value);
|
|
|
|
|
|
|
|
|
|
ctmp = new NetEConst(value);
|
|
|
|
|
ctmp->set_line(*this);
|
|
|
|
|
delete operand;
|
|
|
|
|
return ctmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (expr_wid > (int)operand->expr_width())
|
2008-11-19 02:17:19 +01:00
|
|
|
operand = pad_to_width(operand, expr_wid, *this);
|
2008-10-13 06:38:07 +02:00
|
|
|
|
|
|
|
|
NetEUBits*tmp = new NetEUBits(op_, operand);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
2008-11-10 06:42:12 +01:00
|
|
|
|
|
|
|
|
NetNet* Design::find_discipline_reference(ivl_discipline_t dis, NetScope*scope)
|
|
|
|
|
{
|
|
|
|
|
NetNet*gnd = discipline_references_[dis->name()];
|
|
|
|
|
|
|
|
|
|
if (gnd) return gnd;
|
|
|
|
|
|
|
|
|
|
string name = string(dis->name()) + "$gnd";
|
|
|
|
|
gnd = new NetNet(scope, lex_strings.make(name), NetNet::WIRE, 1);
|
|
|
|
|
gnd->set_discipline(dis);
|
|
|
|
|
gnd->data_type(IVL_VT_REAL);
|
|
|
|
|
discipline_references_[dis->name()] = gnd;
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << gnd->get_fileline() << ": debug: "
|
|
|
|
|
<< "Create an implicit reference terminal"
|
|
|
|
|
<< " for discipline=" << dis->name()
|
|
|
|
|
<< " in scope=" << scope_path(scope) << endl;
|
|
|
|
|
|
|
|
|
|
return gnd;
|
|
|
|
|
}
|