Normalize variable bit/indexed part selects using a fixed routine.
This patch modifies all the variable bit and indexed part selects to use a common routine. This routine determines the minimum width needed to calculate the result correctly, pads the expression if needed and then converts the expression to signed if required to make the calculation correct.
This commit is contained in:
parent
dd4fb9b4ef
commit
fabde6d1b5
61
elab_expr.cc
61
elab_expr.cc
|
|
@ -1011,7 +1011,7 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope,
|
|||
&& wid_left > 0
|
||||
&& wid_left < integer_width) {
|
||||
wid_left = integer_width;
|
||||
|
||||
|
||||
if (debug_elaborate)
|
||||
cerr << get_fileline() << ": debug: "
|
||||
<< "Test width of unsized " << human_readable_op(op_)
|
||||
|
|
@ -1260,7 +1260,7 @@ NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, int wid, bool signed_flag)
|
|||
|
||||
if (wid < 0)
|
||||
wid = expr->expr_width();
|
||||
|
||||
|
||||
if (debug_elaborate)
|
||||
cerr << get_fileline() << ": debug: cast to " << wid
|
||||
<< " bits" << endl;
|
||||
|
|
@ -2481,11 +2481,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope,
|
|||
return result_ex;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
base = normalize_variable_base(base, par_msv, par_lsv, wid, true);
|
||||
|
||||
NetExpr*tmp = par->dup_expr();
|
||||
tmp = new NetESelect(tmp, base, wid);
|
||||
|
|
@ -2565,13 +2561,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope,
|
|||
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);
|
||||
}
|
||||
base = normalize_variable_base(base, par_msv, par_lsv, wid, false);
|
||||
|
||||
NetExpr*tmp = par->dup_expr();
|
||||
tmp = new NetESelect(tmp, base, wid);
|
||||
|
|
@ -2597,7 +2587,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des,
|
|||
if (!name_tail.index.empty())
|
||||
use_sel = name_tail.index.back().sel;
|
||||
|
||||
if (par->expr_type() == IVL_VT_REAL &&
|
||||
if (par->expr_type() == IVL_VT_REAL &&
|
||||
use_sel != index_component_t::SEL_NONE) {
|
||||
perm_string name = peek_tail_name(path_);
|
||||
cerr << get_fileline() << ": error: "
|
||||
|
|
@ -2729,17 +2719,10 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des,
|
|||
} else {
|
||||
|
||||
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);
|
||||
}
|
||||
mtmp = normalize_variable_base(mtmp,
|
||||
par_me->value().as_long(),
|
||||
par_le->value().as_long(),
|
||||
1, true);
|
||||
}
|
||||
|
||||
/* The value is constant, but the bit select
|
||||
|
|
@ -2932,7 +2915,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope,
|
|||
cerr << get_fileline() << ": : "
|
||||
"Replacing select with a constant 'bx." << endl;
|
||||
}
|
||||
|
||||
|
||||
NetEConst*tmp = new NetEConst(verinum(verinum::Vx, 1, false));
|
||||
tmp->set_line(*this);
|
||||
return tmp;
|
||||
|
|
@ -3040,7 +3023,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope,
|
|||
}
|
||||
// Otherwise, make a part select that covers the right
|
||||
// range.
|
||||
ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv) +
|
||||
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;
|
||||
|
|
@ -3079,12 +3062,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope,
|
|||
return ss;
|
||||
}
|
||||
|
||||
if (net->msi() > net->lsi()) {
|
||||
if (long offset = net->lsi())
|
||||
base = make_add_expr(base, -offset);
|
||||
} else {
|
||||
base = make_sub_expr(net->lsi()-wid+1, base);
|
||||
}
|
||||
base = normalize_variable_base(base, net->msi(), net->lsi(), wid, true);
|
||||
|
||||
NetESelect*ss = new NetESelect(net, base, wid);
|
||||
ss->set_line(*this);
|
||||
|
|
@ -3171,12 +3149,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope,
|
|||
return ss;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
base = normalize_variable_base(base, net->msi(), net->lsi(), wid, false);
|
||||
|
||||
NetESelect*ss = new NetESelect(net, base, wid);
|
||||
ss->set_line(*this);
|
||||
|
|
@ -3281,12 +3254,8 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope,
|
|||
// complicated task because we need to generate
|
||||
// expressions to convert calculated bit select
|
||||
// values to canonical values that are used internally.
|
||||
|
||||
if (net->sig()->msb() < net->sig()->lsb()) {
|
||||
ex = make_sub_expr(net->sig()->lsb(), ex);
|
||||
} else {
|
||||
ex = make_add_expr(ex, - net->sig()->lsb());
|
||||
}
|
||||
ex = normalize_variable_base(ex, net->sig()->msb(), net->sig()->lsb(),
|
||||
1, true);
|
||||
|
||||
NetESelect*ss = new NetESelect(net, ex, 1);
|
||||
ss->set_line(*this);
|
||||
|
|
|
|||
26
elab_lval.cc
26
elab_lval.cc
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000-2009 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com)
|
||||
*
|
||||
* This source code is free software; you can redistribute it
|
||||
* and/or modify it in source code form under the terms of the GNU
|
||||
|
|
@ -198,7 +198,7 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
|
|||
if (reg->array_dimensions() > 0)
|
||||
return elaborate_lval_net_word_(des, scope, reg);
|
||||
|
||||
// This must be after the array word elaboration above!
|
||||
// This must be after the array word elaboration above!
|
||||
if (reg->get_scalar() &&
|
||||
use_sel != index_component_t::SEL_NONE) {
|
||||
cerr << get_fileline() << ": error: can not select part of ";
|
||||
|
|
@ -342,7 +342,6 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des,
|
|||
index_tail.msb->test_width(des, scope, integer_width, integer_width,
|
||||
expr_type_tmp, unsized_flag_tmp);
|
||||
|
||||
|
||||
// Bit selects have a single select expression. Evaluate the
|
||||
// constant value and treat it as a part select with a bit
|
||||
// width of 1.
|
||||
|
|
@ -357,10 +356,7 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des,
|
|||
if (mux) {
|
||||
// Non-constant bit mux. Correct the mux for the range
|
||||
// of the vector, then set the l-value part select expression.
|
||||
if (reg->msb() < reg->lsb())
|
||||
mux = make_sub_expr(reg->lsb(), mux);
|
||||
else if (reg->lsb() != 0)
|
||||
mux = make_add_expr(mux, - reg->lsb());
|
||||
mux = normalize_variable_base(mux, reg->msb(), reg->lsb(), 1, true);
|
||||
|
||||
lv->set_part(mux, 1);
|
||||
|
||||
|
|
@ -535,20 +531,12 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des,
|
|||
} else {
|
||||
/* Correct the mux for the range of the vector. */
|
||||
if (use_sel == index_component_t::SEL_IDX_UP) {
|
||||
if (reg->msb() > reg->lsb()) {
|
||||
if (long offset = reg->lsb())
|
||||
base = make_add_expr(base, -offset);
|
||||
} else {
|
||||
base = make_sub_expr(reg->lsb()-wid+1, base);
|
||||
}
|
||||
base = normalize_variable_base(base, reg->msb(), reg->lsb(),
|
||||
wid, true);
|
||||
} else {
|
||||
// This is assumed to be a SEL_IDX_DO.
|
||||
if (reg->msb() > reg->lsb()) {
|
||||
if (long offset = reg->lsb()+wid-1)
|
||||
base = make_add_expr(base, -offset);
|
||||
} else {
|
||||
base = make_sub_expr(reg->lsb(), base);
|
||||
}
|
||||
base = normalize_variable_base(base, reg->msb(), reg->lsb(),
|
||||
wid, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
129
netmisc.cc
129
netmisc.cc
|
|
@ -156,6 +156,120 @@ NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src)
|
|||
return tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subtract an existing expression from a signed constant.
|
||||
*/
|
||||
static NetExpr* make_sub_expr(long val, NetExpr*expr)
|
||||
{
|
||||
verinum val_v (val, expr->expr_width());
|
||||
val_v.has_sign(true);
|
||||
|
||||
NetEConst*val_c = new NetEConst(val_v);
|
||||
val_c->set_line(*expr);
|
||||
|
||||
NetEBAdd*res = new NetEBAdd('-', val_c, expr);
|
||||
res->set_line(*expr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is used to calculate the number of bits needed to
|
||||
* contain the given number.
|
||||
*/
|
||||
static unsigned num_bits(long arg)
|
||||
{
|
||||
unsigned res = 0;
|
||||
|
||||
/* For a negative value we have room for one extra value, but
|
||||
* we have a signed result so we need an extra bit for this. */
|
||||
if (arg < 0) {
|
||||
arg = -arg - 1;
|
||||
res += 1;
|
||||
}
|
||||
|
||||
/* Calculate the number of bits needed here. */
|
||||
while (arg) {
|
||||
res += 1;
|
||||
arg >>= 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine generates the normalization expression needed for a variable
|
||||
* bit select or a variable base expression for an indexed part select.
|
||||
*/
|
||||
NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb,
|
||||
unsigned long wid, bool is_up)
|
||||
{
|
||||
long offset = lsb;
|
||||
|
||||
if (msb < lsb) {
|
||||
/* Correct the offset if needed. */
|
||||
if (is_up) offset -= wid - 1;
|
||||
/* Calculate the space needed for the offset. */
|
||||
unsigned min_wid = num_bits(offset);
|
||||
/* We need enough space for the larger of the offset or the
|
||||
* base expression. */
|
||||
if (min_wid < base->expr_width()) min_wid = base->expr_width();
|
||||
/* Now that we have the minimum needed width increase it by
|
||||
* one to make room for the normalization calculation. */
|
||||
min_wid += 1;
|
||||
/* Pad the base expression to the correct width. */
|
||||
base = pad_to_width(base, min_wid, *base);
|
||||
/* If the base expression is unsigned and either the lsb
|
||||
* is negative or it does not fill the width of the base
|
||||
* expression then we could generate negative normalized
|
||||
* values so cast the expression to signed to get the
|
||||
* math correct. */
|
||||
if ((lsb < 0 || num_bits(lsb+1) <= base->expr_width()) &&
|
||||
! base->has_sign()) {
|
||||
/* We need this extra select to hide the signed
|
||||
* property from the padding above. It will be
|
||||
* removed automatically during code generation. */
|
||||
NetESelect *tmp = new NetESelect(base, 0 , min_wid);
|
||||
tmp->set_line(*base);
|
||||
tmp->cast_signed(true);
|
||||
base = tmp;
|
||||
}
|
||||
/* Normalize the expression. */
|
||||
base = make_sub_expr(offset, base);
|
||||
} else {
|
||||
/* Correct the offset if needed. */
|
||||
if (!is_up) offset += wid - 1;
|
||||
/* If the offset is zero then just return the base (index)
|
||||
* expression. */
|
||||
if (offset == 0) return base;
|
||||
/* Calculate the space needed for the offset. */
|
||||
unsigned min_wid = num_bits(-offset);
|
||||
/* We need enough space for the larger of the offset or the
|
||||
* base expression. */
|
||||
if (min_wid < base->expr_width()) min_wid = base->expr_width();
|
||||
/* Now that we have the minimum needed width increase it by
|
||||
* one to make room for the normalization calculation. */
|
||||
min_wid += 1;
|
||||
/* Pad the base expression to the correct width. */
|
||||
base = pad_to_width(base, min_wid, *base);
|
||||
/* If the offset is greater than zero then we need to do
|
||||
* signed math to get the location value correct. */
|
||||
if (offset > 0 && ! base->has_sign()) {
|
||||
/* We need this extra select to hide the signed
|
||||
* property from the padding above. It will be
|
||||
* removed automatically during code generation. */
|
||||
NetESelect *tmp = new NetESelect(base, 0 , min_wid);
|
||||
tmp->set_line(*base);
|
||||
tmp->cast_signed(true);
|
||||
base = tmp;
|
||||
}
|
||||
/* Normalize the expression. */
|
||||
base = make_add_expr(base, -offset);
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a signed constant to an existing expression. Generate a new
|
||||
* NetEBAdd node that has the input expression and an expression made
|
||||
|
|
@ -190,19 +304,6 @@ NetExpr* make_add_expr(NetExpr*expr, long val)
|
|||
return res;
|
||||
}
|
||||
|
||||
NetExpr* make_sub_expr(long val, NetExpr*expr)
|
||||
{
|
||||
verinum val_v (val, expr->expr_width());
|
||||
val_v.has_sign(true);
|
||||
NetEConst*val_c = new NetEConst(val_v);
|
||||
val_c->set_line(*expr);
|
||||
|
||||
NetEBAdd*res = new NetEBAdd('-', val_c, expr);
|
||||
res->set_line(*expr);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
NetEConst* make_const_x(unsigned long wid)
|
||||
{
|
||||
verinum xxx (verinum::Vx, wid);
|
||||
|
|
@ -430,7 +531,7 @@ const char *human_readable_op(const char op, bool unary)
|
|||
case '>': type = ">"; break;
|
||||
case 'L': type = "<="; break;
|
||||
case 'G': type = ">="; break;
|
||||
|
||||
|
||||
case '^': type = "^"; break; // XOR
|
||||
case 'X': type = "~^"; break; // XNOR
|
||||
case '&': type = "&"; break; // Bitwise AND
|
||||
|
|
|
|||
16
netmisc.h
16
netmisc.h
|
|
@ -91,6 +91,13 @@ extern NetExpr*condition_reduce(NetExpr*expr);
|
|||
*/
|
||||
extern NetNet*crop_to_width(Design*des, NetNet*n, unsigned w);
|
||||
|
||||
/*
|
||||
* This function generates an equation to normalize an expression using
|
||||
* the provided array/vector information.
|
||||
*/
|
||||
extern NetExpr*normalize_variable_base(NetExpr *base, long msb, long lsb,
|
||||
unsigned long wid, bool is_up);
|
||||
|
||||
/*
|
||||
* This function takes as input a NetNet signal and adds a constant
|
||||
* value to it. If the val is 0, then simply return sig. Otherwise,
|
||||
|
|
@ -100,21 +107,12 @@ extern NetNet*add_to_net(Design*des, NetNet*sig, long val);
|
|||
extern NetNet*sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig);
|
||||
|
||||
/*
|
||||
* These functions make various sorts of expressions, given operands
|
||||
* of certain type. The order of the operands is preserved in cases
|
||||
* where order matters.
|
||||
*
|
||||
* make_add_expr
|
||||
* Make a NetEBAdd expression with <expr> the first argument and
|
||||
* <val> the second. This may get turned into a subtract if <val> is
|
||||
* less than zero. If val is exactly zero, then return <expr> as is.
|
||||
*
|
||||
* make_sub_expr
|
||||
* Make a NetEBAdd(subtract) node that subtracts the given
|
||||
* expression from the integer value.
|
||||
*/
|
||||
extern NetExpr*make_add_expr(NetExpr*expr, long val);
|
||||
extern NetExpr*make_sub_expr(long val, NetExpr*expr);
|
||||
|
||||
/*
|
||||
* Make a NetEConst object that contains only X bits.
|
||||
|
|
|
|||
Loading…
Reference in New Issue