Skip to content

Commit

Permalink
V0.9: Normalize variable bit/indexed part selects using a fixed routine.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
caryr authored and steveicarus committed Sep 19, 2010
1 parent 3333a5e commit e6b04cc
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 90 deletions.
61 changes: 15 additions & 46 deletions elab_expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,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_)
Expand Down Expand Up @@ -1250,7 +1250,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;
Expand Down Expand Up @@ -2486,11 +2486,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);
Expand Down Expand Up @@ -2570,13 +2566,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);
Expand All @@ -2602,7 +2592,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: "
Expand Down Expand Up @@ -2734,17 +2724,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
Expand Down Expand Up @@ -2937,7 +2920,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;
Expand Down Expand Up @@ -3045,7 +3028,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;
Expand Down Expand Up @@ -3084,12 +3067,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);
Expand Down Expand Up @@ -3176,12 +3154,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);
Expand Down Expand Up @@ -3286,12 +3259,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);
Expand Down
26 changes: 7 additions & 19 deletions elab_lval.cc
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 ";
Expand Down Expand Up @@ -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.
Expand All @@ -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);

Expand Down Expand Up @@ -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);
}
}

Expand Down
153 changes: 138 additions & 15 deletions netmisc.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2001-2009 Stephen Williams (steve@icarus.com)
* Copyright (c) 2001-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
Expand Down Expand Up @@ -156,6 +156,142 @@ 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;
/* V0.9 does not handle small signed vectors correctly so
* increase the vector size to make this work. */
if (base->has_sign() && min_wid < 8*sizeof(int)) {
min_wid = 8*sizeof(int);
}
/* 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()) {
/* 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_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;
/* V0.9 does not handle small signed vectors correctly so
* increase the vector size to make this work. */
if (base->has_sign() && min_wid < 8*sizeof(int)) {
min_wid = 8*sizeof(int);
}
/* 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);
}

return base;
}

/*
* Add a signed constant to an existing expression. Generate a new
* NetEBAdd node that has the input expression and an expression made
Expand Down Expand Up @@ -190,19 +326,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);
Expand Down Expand Up @@ -429,7 +552,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
Expand Down
Loading

0 comments on commit e6b04cc

Please sign in to comment.