Pad and sign convert array index expressions as needed.

This patch mimics what was done for vectors, but is simpler since
arrays don't use the endian information. It also needs to address
the fact that .array/port assumes the expression is unsigned so
any signed expression must be padded to make it larger than the
maximum array word when it is converted to unsigned.
This commit is contained in:
Cary R 2010-09-17 10:43:11 -07:00 committed by Stephen Williams
parent fabde6d1b5
commit f522ca8a9c
4 changed files with 81 additions and 38 deletions

View File

@ -2839,7 +2839,8 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope,
// expression to calculate the canonical address. // expression to calculate the canonical address.
if (long base = net->array_first()) { if (long base = net->array_first()) {
word_index = make_add_expr(word_index, 0-base); word_index = normalize_variable_array_base(
word_index, base, net->array_count());
eval_expr(word_index); eval_expr(word_index);
} }
} }

View File

@ -269,7 +269,8 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des,
// expression to calculate the canonical address. // expression to calculate the canonical address.
if (long base = reg->array_first()) { if (long base = reg->array_first()) {
word = make_add_expr(word, 0-base); word = normalize_variable_array_base(word, base,
reg->array_count());
eval_expr(word); eval_expr(word);
} }

View File

@ -156,6 +156,40 @@ NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src)
return tmp; return tmp;
} }
/*
* Add a signed constant to an existing expression. Generate a new
* NetEBAdd node that has the input expression and an expression made
* from the constant value.
*/
static NetExpr* make_add_expr(NetExpr*expr, long val)
{
if (val == 0)
return expr;
// If the value to be added is <0, then instead generate a
// SUBTRACT node and turn the value positive.
char add_op = '+';
if (val < 0) {
add_op = '-';
val = -val;
}
verinum val_v (val);
val_v.has_sign(true);
if (expr->has_width()) {
val_v = verinum(val_v, expr->expr_width());
}
NetEConst*val_c = new NetEConst(val_v);
val_c->set_line(*expr);
NetEBAdd*res = new NetEBAdd(add_op, expr, val_c);
res->set_line(*expr);
return res;
}
/* /*
* Subtract an existing expression from a signed constant. * Subtract an existing expression from a signed constant.
*/ */
@ -271,37 +305,50 @@ NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb,
} }
/* /*
* Add a signed constant to an existing expression. Generate a new * This routine generates the normalization expression needed for a variable
* NetEBAdd node that has the input expression and an expression made * array word select.
* from the constant value.
*/ */
NetExpr* make_add_expr(NetExpr*expr, long val) NetExpr *normalize_variable_array_base(NetExpr *base, long offset,
unsigned count)
{ {
if (val == 0) assert(offset != 0);
return expr; /* 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);
// If the value to be added is <0, then instead generate a /* We should not need to do this, but .array/port does not
// SUBTRACT node and turn the value positive. * handle a small signed index correctly and it is a major
char add_op = '+'; * effort to fix it. For now we will just pad the expression
if (val < 0) { * enough so that any negative value when converted to
add_op = '-'; * unsigned is larger than the maximum array word. */
val = -val; if (base->has_sign()) {
unsigned range_wid = num_bits(count-1) + 1;
if (min_wid < range_wid) {
base = pad_to_width(base, range_wid, *base);
}
} }
verinum val_v (val); return base;
val_v.has_sign(true);
if (expr->has_width()) {
val_v = verinum(val_v, expr->expr_width());
}
NetEConst*val_c = new NetEConst(val_v);
val_c->set_line(*expr);
NetEBAdd*res = new NetEBAdd(add_op, expr, val_c);
res->set_line(*expr);
return res;
} }
NetEConst* make_const_x(unsigned long wid) NetEConst* make_const_x(unsigned long wid)

View File

@ -92,11 +92,13 @@ extern NetExpr*condition_reduce(NetExpr*expr);
extern NetNet*crop_to_width(Design*des, NetNet*n, unsigned w); extern NetNet*crop_to_width(Design*des, NetNet*n, unsigned w);
/* /*
* This function generates an equation to normalize an expression using * These functions generate an equation to normalize an expression using
* the provided array/vector information. * the provided vector/array information.
*/ */
extern NetExpr*normalize_variable_base(NetExpr *base, long msb, long lsb, extern NetExpr*normalize_variable_base(NetExpr *base, long msb, long lsb,
unsigned long wid, bool is_up); unsigned long wid, bool is_up);
extern NetExpr*normalize_variable_array_base(NetExpr *base, long offset,
unsigned count);
/* /*
* This function takes as input a NetNet signal and adds a constant * This function takes as input a NetNet signal and adds a constant
@ -106,14 +108,6 @@ extern NetExpr*normalize_variable_base(NetExpr *base, long msb, long lsb,
extern NetNet*add_to_net(Design*des, NetNet*sig, long val); extern NetNet*add_to_net(Design*des, NetNet*sig, long val);
extern NetNet*sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig); extern NetNet*sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig);
/*
* 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.
*/
extern NetExpr*make_add_expr(NetExpr*expr, long val);
/* /*
* Make a NetEConst object that contains only X bits. * Make a NetEConst object that contains only X bits.
*/ */