From 331faa22173d8a3ede93a8117b35b9723155eb51 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 8 Feb 2008 17:32:57 -0800 Subject: [PATCH] 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. --- elab_net.cc | 12 +++--------- net_expr.cc | 5 +++-- set_width.cc | 6 ++---- t-dll.cc | 1 + tgt-vvp/vvp_scope.c | 13 ++++++++++--- vvp/arith.cc | 34 ++++++++++++++++++++++++---------- vvp/arith.h | 4 +++- vvp/compile.cc | 19 +++++++++++++++---- vvp/compile.h | 2 +- vvp/lexor.lex | 1 + vvp/parse.y | 9 +++++++-- vvp/vvp_net.cc | 26 ++++++++++++++++++++++++++ vvp/vvp_net.h | 2 ++ 13 files changed, 98 insertions(+), 36 deletions(-) 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