Add signed bit based power to continuous assignments.

This patch adds the power operator for signed bit based values
in a continuous assignment. It also fixes a few other power
expression width problems. The expression width is still not
calculated correctly, since the correct method can produce huge
possible bit widths. The result is currently limited to the width
of the native long. This is because lround() is used to convert
from a double to an integer. A check in the code generator protects
the runtime from this limitation.
This commit is contained in:
Cary R 2008-02-08 17:32:57 -08:00 committed by Stephen Williams
parent f2ff25bfef
commit 331faa2217
13 changed files with 98 additions and 36 deletions

View File

@ -1142,14 +1142,6 @@ NetNet* PEBinary::elaborate_net_pow_(Design*des, NetScope*scope,
// The power is signed if either its operands are signed.
bool arith_is_signed = lsig->get_signed() || rsig->get_signed();
/* For now we only support real values. */
if (lsig->data_type() != IVL_VT_REAL && arith_is_signed) {
cerr << get_fileline() << ": sorry: Signed bit based power (**) is "
<< "currently unsupported in continuous assignments." << endl;
des->errors += 1;
return 0;
}
unsigned rwidth = lwidth;
if (rwidth == 0) {
/* Reals are always 1 wide and lsig/rsig types match here. */
@ -1157,7 +1149,9 @@ NetNet* PEBinary::elaborate_net_pow_(Design*des, NetScope*scope,
rwidth = 1;
lwidth = 1;
} else {
/* Nothing for now. Need integer value.*/
/* This is incorrect! a * (2^b - 1) is close. */
rwidth = lsig->vector_width() + rsig->vector_width();
lwidth = rwidth;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002-2007 Stephen Williams (steve@icarus.com)
* Copyright (c) 2002-2008 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
@ -258,7 +258,8 @@ NetEBPow::NetEBPow(char op, NetExpr*l, NetExpr*r)
: NetEBinary(op, l, r)
{
assert(op == 'p');
expr_width(l->expr_width());
/* This is incorrect! a * (2^b - 1) is close. */
expr_width(l->expr_width()+r->expr_width());
cast_signed(l->has_sign() || r->has_sign());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2007 Stephen Williams (steve@icarus.com)
* Copyright (c) 1999-2008 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
@ -185,8 +185,7 @@ bool NetEBMult::set_width(unsigned w, bool)
bool NetEBPow::set_width(unsigned w, bool last_chance)
{
bool flag = left_->set_width(w, last_chance);
return flag;
return w == expr_width();
}
/*
@ -445,4 +444,3 @@ bool NetEUReduce::set_width(unsigned w, bool)
{
return w == 1;
}

View File

@ -1777,6 +1777,7 @@ void dll_target::lpm_pow(const NetPow*net)
{
ivl_lpm_t obj = new struct ivl_lpm_s;
obj->type = IVL_LPM_POW;
FILE_NAME(obj, net);
obj->name = net->name();
assert(net->scope());
obj->scope = find_scope(des_, net->scope());

View File

@ -1708,9 +1708,16 @@ static void draw_lpm_add(ivl_lpm_t net)
case IVL_LPM_POW:
if (dto == IVL_VT_REAL)
type = "pow.r";
else if (ivl_lpm_signed(net))
assert(0); /* No support for signed bit based signals. */
else
else if (ivl_lpm_signed(net)) {
type = "pow.s";
if (width > 8*sizeof(long)) {
fprintf(stderr, "%s:%u: sorry (vvp-tgt): Signed power "
"result must be no more than %d bits.\n",
ivl_lpm_file(net), ivl_lpm_lineno(net),
8*sizeof(long));
exit(1);
}
} else
type = "pow";
break;
default:

View File

@ -400,8 +400,8 @@ void vvp_arith_mult::wide(vvp_ipoint_t base, bool push)
// Power
vvp_arith_pow::vvp_arith_pow(unsigned wid)
: vvp_arith_(wid)
vvp_arith_pow::vvp_arith_pow(unsigned wid, bool signed_flag)
: vvp_arith_(wid), signed_flag_(signed_flag)
{
}
@ -413,17 +413,31 @@ void vvp_arith_pow::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit)
{
dispatch_operand_(ptr, bit);
vvp_vector2_t a2 (op_a_);
vvp_vector2_t b2 (op_b_);
vvp_vector4_t res4;
if (signed_flag_) {
if (op_a_.has_xz() || op_b_.has_xz()) {
vvp_send_vec4(ptr.ptr()->out, x_val_);
return;
}
if (a2.is_NaN() || b2.is_NaN()) {
vvp_send_vec4(ptr.ptr()->out, x_val_);
return;
double ad, bd;
vector4_to_value(op_a_, ad, true);
vector4_to_value(op_b_, bd, true);
res4 = double_to_vector4(pow(ad, bd), wid_);
} else {
vvp_vector2_t a2 (op_a_);
vvp_vector2_t b2 (op_b_);
if (a2.is_NaN() || b2.is_NaN()) {
vvp_send_vec4(ptr.ptr()->out, x_val_);
return;
}
vvp_vector2_t result = pow(a2, b2);
res4 = vector2_to_vector4(result, wid_);
}
vvp_vector2_t result = pow(a2, b2);
vvp_vector4_t res4 = vector2_to_vector4(result, wid_);
vvp_send_vec4(ptr.ptr()->out, res4);
}

View File

@ -162,9 +162,11 @@ class vvp_arith_mult : public vvp_arith_ {
class vvp_arith_pow : public vvp_arith_ {
public:
explicit vvp_arith_pow(unsigned wid);
explicit vvp_arith_pow(unsigned wid, bool signed_flag);
~vvp_arith_pow();
void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit);
private:
bool signed_flag_;
};
class vvp_arith_sub : public vvp_arith_ {

View File

@ -921,7 +921,10 @@ void compile_arith_div(char*label, long wid, bool signed_flag,
assert( wid > 0 );
if (argc != 2) {
fprintf(stderr, "%s; .arith/div has wrong number of symbols\n", label);
char *suffix = "";
if (signed_flag) suffix = ".s";
fprintf(stderr, "%s; .arith/div%s has wrong number of "
"symbols\n", label, suffix);
compile_errors += 1;
return;
}
@ -998,18 +1001,26 @@ void compile_arith_mult_r(char*label, unsigned argc, struct symb_s*argv)
}
void compile_arith_pow(char*label, long wid,
void compile_arith_pow(char*label, long wid, bool signed_flag,
unsigned argc, struct symb_s*argv)
{
assert( wid > 0 );
/* For now we need to do a double to long cast, so the number
of bits is limited. This should be caught in the compiler. */
if (signed_flag) {
assert( wid <= (long)(8*sizeof(long)) );
}
if (argc != 2) {
fprintf(stderr, "%s .arith/pow has wrong number of symbols\n", label);
char *suffix = "";
if (signed_flag) suffix = ".s";
fprintf(stderr, "%s .arith/pow%s has wrong number of "
"symbols\n", label, suffix);
compile_errors += 1;
return;
}
vvp_arith_ *arith = new vvp_arith_pow(wid);
vvp_arith_ *arith = new vvp_arith_pow(wid, signed_flag);
make_arith(arith, label, argc, argv);
}

View File

@ -146,7 +146,7 @@ extern void compile_part_select_var(char*label, char*src,
* This is called by the parser to make the various arithmetic and
* comparison functors.
*/
extern void compile_arith_pow(char*label, long width,
extern void compile_arith_pow(char*label, long width, bool signed_flag,
unsigned argc, struct symb_s*argv);
extern void compile_arith_div(char*label, long width, bool signed_flag,
unsigned argc, struct symb_s*argv);

View File

@ -96,6 +96,7 @@
".arith/mult.r" { return K_ARITH_MULT_R; }
".arith/pow" { return K_ARITH_POW; }
".arith/pow.r" { return K_ARITH_POW_R; }
".arith/pow.s" { return K_ARITH_POW_S; }
".arith/sub" { return K_ARITH_SUB; }
".arith/sub.r" { return K_ARITH_SUB_R; }
".arith/sum" { return K_ARITH_SUM; }

View File

@ -68,7 +68,7 @@ static struct __vpiModPath*modpath_dst = 0;
%token K_ALIAS K_ALIAS_S K_ALIAS_R
%token K_ARITH_DIV K_ARITH_DIV_R K_ARITH_DIV_S K_ARITH_MOD K_ARITH_MOD_R
%token K_ARITH_MULT K_ARITH_MULT_R K_ARITH_SUB K_ARITH_SUB_R
%token K_ARITH_SUM K_ARITH_SUM_R K_ARITH_POW K_ARITH_POW_R
%token K_ARITH_SUM K_ARITH_SUM_R K_ARITH_POW K_ARITH_POW_R K_ARITH_POW_S
%token K_ARRAY K_ARRAY_I K_ARRAY_R K_ARRAY_S K_ARRAY_PORT
%token K_CMP_EEQ K_CMP_EQ K_CMP_EQ_R K_CMP_NEE K_CMP_NE K_CMP_NE_R
%token K_CMP_GE K_CMP_GE_R K_CMP_GE_S K_CMP_GT K_CMP_GT_R K_CMP_GT_S
@ -288,7 +288,7 @@ statement
| T_LABEL K_ARITH_POW T_NUMBER ',' symbols ';'
{ struct symbv_s obj = $5;
compile_arith_pow($1, $3, obj.cnt, obj.vect);
compile_arith_pow($1, $3, false, obj.cnt, obj.vect);
}
| T_LABEL K_ARITH_POW_R T_NUMBER ',' symbols ';'
@ -296,6 +296,11 @@ statement
compile_arith_pow_r($1, obj.cnt, obj.vect);
}
| T_LABEL K_ARITH_POW_S T_NUMBER ',' symbols ';'
{ struct symbv_s obj = $5;
compile_arith_pow($1, $3, true, obj.cnt, obj.vect);
}
| T_LABEL K_ARITH_SUB T_NUMBER ',' symbols ';'
{ struct symbv_s obj = $5;
compile_arith_sub($1, $3, obj.cnt, obj.vect);

View File

@ -697,6 +697,32 @@ ostream& operator<< (ostream&out, const vvp_vector4_t&that)
return out;
}
/* The width is guaranteed to not be larger than a long.
* If the double is outside the integer range (+/-) the
* largest/smallest integer value is returned. */
vvp_vector4_t double_to_vector4(double val, unsigned wid)
{
long span = 1l << (wid-1);
double dmin = -1l * span;
double dmax = span - 1l;
if (val > dmax) val = dmax;
if (val < dmin) val = dmin;
vvp_vector4_t res (wid);
long bits = lround(val);
for (unsigned idx = 0 ; idx < wid ; idx += 1) {
vvp_bit4_t bit = BIT4_0;
if (bits & 1L) bit = BIT4_1;
res.set_bit(idx, bit);
bits >>= 1;
}
return res;
}
bool vector4_to_value(const vvp_vector4_t&vec, unsigned long&val)
{
unsigned long res = 0;

View File

@ -258,6 +258,8 @@ extern vvp_bit4_t compare_gtge_signed(const vvp_vector4_t&a,
vvp_bit4_t val_if_equal);
template <class T> extern T coerce_to_width(const T&that, unsigned width);
extern vvp_vector4_t double_to_vector4(double val, unsigned wid);
/*
* These functions extract the value of the vector as a native type,
* if possible, and return true to indicate success. If the vector has