diff --git a/elab_net.cc b/elab_net.cc index 6ee8add8a..a2ff432cd 100644 --- a/elab_net.cc +++ b/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; } } diff --git a/net_expr.cc b/net_expr.cc index 0bd86acff..73dd924f6 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -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()); } diff --git a/set_width.cc b/set_width.cc index 4dcc72e44..8b3257340 100644 --- a/set_width.cc +++ b/set_width.cc @@ -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; } - diff --git a/t-dll.cc b/t-dll.cc index 958cbdc46..2e2e06316 100644 --- a/t-dll.cc +++ b/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()); diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index b90ac15d8..aae63e41c 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -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: diff --git a/vvp/arith.cc b/vvp/arith.cc index a984d68d6..7ee51a451 100644 --- a/vvp/arith.cc +++ b/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); } diff --git a/vvp/arith.h b/vvp/arith.h index bd238edc3..acc85b263 100644 --- a/vvp/arith.h +++ b/vvp/arith.h @@ -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_ { diff --git a/vvp/compile.cc b/vvp/compile.cc index e7337d8bc..c826f5910 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -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); } diff --git a/vvp/compile.h b/vvp/compile.h index 92270a794..49da17e13 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -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); diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 93641eb9d..c0513c65a 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -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; } diff --git a/vvp/parse.y b/vvp/parse.y index 7fda09d4d..9cd696016 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -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); diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index 2da64524a..8f54bf05f 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -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; diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index 7f32ade93..8b163f803 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -258,6 +258,8 @@ extern vvp_bit4_t compare_gtge_signed(const vvp_vector4_t&a, vvp_bit4_t val_if_equal); template 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