diff --git a/elab_expr.cc b/elab_expr.cc index f0ed6ffc9..57ab64ae7 100644 --- a/elab_expr.cc +++ b/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); diff --git a/elab_lval.cc b/elab_lval.cc index f3ca7d083..9d2f38d32 100644 --- a/elab_lval.cc +++ b/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); } } diff --git a/netmisc.cc b/netmisc.cc index 8bde89a53..76ae01950 100644 --- a/netmisc.cc +++ b/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 diff --git a/netmisc.h b/netmisc.h index 31d97def7..9ca563574 100644 --- a/netmisc.h +++ b/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 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. - * - * 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.