From 633006bc6e2596af04d49ce1d9358522bcfd7587 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 17 Sep 2010 11:11:05 -0700 Subject: [PATCH] V0.9: 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. --- elab_expr.cc | 3 +- elab_lval.cc | 3 +- netmisc.cc | 105 ++++++++++++++++++++++++++++++++++++++------------- netmisc.h | 14 ++----- 4 files changed, 87 insertions(+), 38 deletions(-) diff --git a/elab_expr.cc b/elab_expr.cc index 7bc20d2c8..efc07e8ab 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -2844,7 +2844,8 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, // expression to calculate the canonical address. 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); } } diff --git a/elab_lval.cc b/elab_lval.cc index 9d2f38d32..cb7bfd2fb 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -269,7 +269,8 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, // expression to calculate the canonical address. 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); } diff --git a/netmisc.cc b/netmisc.cc index 58575319c..fb0656f2a 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -156,6 +156,40 @@ NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src) 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. */ @@ -293,37 +327,56 @@ NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb, } /* - * 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. + * This routine generates the normalization expression needed for a variable + * array word select. */ -NetExpr* make_add_expr(NetExpr*expr, long val) +NetExpr *normalize_variable_array_base(NetExpr *base, long offset, + unsigned count) { - if (val == 0) - return expr; + assert(offset != 0); + /* 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()) { + /* V0.9 does not handle small signed vectors correctly + * so increase the vector size to make this work. */ + if (min_wid < 8*sizeof(int)) { + min_wid = 8*sizeof(int); + base = pad_to_width(base, min_wid, *base); + } + /* 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 - // SUBTRACT node and turn the value positive. - char add_op = '+'; - if (val < 0) { - add_op = '-'; - val = -val; + /* We should not need to do this, but .array/port does not + * handle a small signed index correctly and it is a major + * effort to fix it. For now we will just pad the expression + * enough so that any negative value when converted to + * unsigned is larger than the maximum array word. */ + 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); - 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; + return base; } NetEConst* make_const_x(unsigned long wid) diff --git a/netmisc.h b/netmisc.h index 326e05ab5..3033344d9 100644 --- a/netmisc.h +++ b/netmisc.h @@ -92,11 +92,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. + * These functions generate an equation to normalize an expression using + * the provided vector/array information. */ extern NetExpr*normalize_variable_base(NetExpr *base, long msb, long lsb, 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 @@ -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*sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig); -/* - * make_add_expr - * Make a NetEBAdd expression with the first argument and - * the second. This may get turned into a subtract if is - * less than zero. If val is exactly zero, then return as is. - */ -extern NetExpr*make_add_expr(NetExpr*expr, long val); - /* * Make a NetEConst object that contains only X bits. */