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:
parent
f2ff25bfef
commit
331faa2217
12
elab_net.cc
12
elab_net.cc
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
1
t-dll.cc
1
t-dll.cc
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
34
vvp/arith.cc
34
vvp/arith.cc
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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_ {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue