From bbdf622ea5558bec1f31306693198c0059f80614 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 7 Nov 2008 18:23:04 -0800 Subject: [PATCH] Fix numerous problems with the divide and modulus operators. This patch fixes a number of problems related to the divide and modulus operators. The net version (CA) of modulus did not support a signed version. Division or modulus of a value wider than the machine word did not correctly check for division by zero and return 'bx. Fixed a problem in procedural modulus. The sign of the result is only dependent on the L-value. Division or modulus of a signed value that was the same width as the machine word was creating an incorrect sign mask. Division of a signed value that would fit into a single machine word was not checking for division by zero. Division or modulus of a wide value was always being done as unsigned. Added a negative operator for vvp_vector2_t. This made implementing the signed wide division and modulus easier. --- expr_synth.cc | 1 + net_modulo.cc | 37 ++++++++------------------ netlist.h | 5 ++++ t-dll.cc | 2 +- tgt-vvp/vvp_scope.c | 2 ++ vvp/arith.cc | 65 ++++++++++++++++++++++++++++++++++++++------- vvp/compile.cc | 4 +-- vvp/compile.h | 2 +- vvp/lexor.lex | 1 + vvp/parse.y | 9 +++++-- vvp/vthread.cc | 2 +- vvp/vvp_net.cc | 15 +++++++++++ vvp/vvp_net.h | 2 ++ 13 files changed, 105 insertions(+), 42 deletions(-) diff --git a/expr_synth.cc b/expr_synth.cc index c1b7196ec..ff829aacf 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -484,6 +484,7 @@ NetNet* NetEBDiv::synthesize(Design*des, NetScope*scope) lsig->vector_width(), rsig->vector_width()); div->set_line(*this); + div->set_signed(has_sign()); des->add_node(div); connect(div->pin_DataA(), lsig->pin(0)); diff --git a/net_modulo.cc b/net_modulo.cc index de99a4dba..13cd262d7 100644 --- a/net_modulo.cc +++ b/net_modulo.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-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 @@ -16,9 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: net_modulo.cc,v 1.9 2005/09/15 22:54:47 steve Exp $" -#endif # include "config.h" @@ -69,6 +66,16 @@ Link& NetModulo::pin_Result() return pin(0); } +void NetModulo::set_signed(bool flag) +{ + signed_flag_ = flag; +} + +bool NetModulo::get_signed() const +{ + return signed_flag_; +} + const Link& NetModulo::pin_Result() const { return pin(0); @@ -93,25 +100,3 @@ const Link& NetModulo::pin_DataB() const { return pin(2); } - -/* - * $Log: net_modulo.cc,v $ - * Revision 1.9 2005/09/15 22:54:47 steve - * Fix bug configuring NetModulo pins. - * - * Revision 1.8 2005/03/12 06:43:35 steve - * Update support for LPM_MOD. - * - * Revision 1.7 2004/02/18 17:11:56 steve - * Use perm_strings for named langiage items. - * - * Revision 1.6 2003/03/06 00:28:41 steve - * All NetObj objects have lex_string base names. - * - * Revision 1.5 2002/08/12 01:34:59 steve - * conditional ident string using autoconfig. - * - * Revision 1.4 2002/08/11 23:47:04 steve - * Add missing Log and Ident strings. - * - */ diff --git a/netlist.h b/netlist.h index 43ea14d0c..0fa9b274f 100644 --- a/netlist.h +++ b/netlist.h @@ -1177,6 +1177,9 @@ class NetModulo : public NetNode { unsigned width_a() const; unsigned width_b() const; + void set_signed(bool); + bool get_signed() const; + Link& pin_DataA(); Link& pin_DataB(); Link& pin_Result(); @@ -1193,6 +1196,8 @@ class NetModulo : public NetNode { unsigned width_r_; unsigned width_a_; unsigned width_b_; + + bool signed_flag_; }; /* diff --git a/t-dll.cc b/t-dll.cc index 37a949270..d70a94d13 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1782,7 +1782,7 @@ void dll_target::lpm_modulo(const NetModulo*net) unsigned wid = net->width_r(); obj->width = wid; - obj->u_.arith.signed_flag = 0; + obj->u_.arith.signed_flag = net->get_signed()? 1 : 0; const Nexus*nex; diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 3eb57d22e..64ab70e3d 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1170,6 +1170,8 @@ static void draw_lpm_add(ivl_lpm_t net) case IVL_LPM_MOD: if (dto == IVL_VT_REAL) type = "mod.r"; + else if (ivl_lpm_signed(net)) + type = "mod.s"; else type = "mod"; break; diff --git a/vvp/arith.cc b/vvp/arith.cc index 3945ea076..424e204e7 100644 --- a/vvp/arith.cc +++ b/vvp/arith.cc @@ -144,13 +144,25 @@ void vvp_arith_div::wide4_(vvp_net_ptr_t ptr) } vvp_vector2_t b2 (op_b_); - if (b2.is_NaN()) { + if (b2.is_NaN() || b2.is_zero()) { vvp_send_vec4(ptr.ptr()->out, x_val_, 0); return; } - vvp_vector2_t res2 = a2 / b2; - vvp_send_vec4(ptr.ptr()->out, vector2_to_vector4(res2, wid_), 0); + bool negate = false; + if (signed_flag_) { + if (a2.value(a2.size()-1)) { + a2 = -a2; + negate = true; + } + if (b2.value(b2.size()-1)) { + b2 = -b2; + negate = !negate; + } + } + vvp_vector2_t res = a2 / b2; + if (negate) res = -res; + vvp_send_vec4(ptr.ptr()->out, vector2_to_vector4(res, wid_), 0); } void vvp_arith_div::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, @@ -180,17 +192,34 @@ void vvp_arith_div::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, the operands for now, and remember to put the sign back later. */ if (signed_flag_) { + unsigned long sign_mask = 0; + if (op_a_.size() != 8 * sizeof(unsigned long)) { + sign_mask = -1UL << op_a_.size(); + } if (op_a_.value(op_a_.size()-1)) { - a = (-a) & ~ (-1UL << op_a_.size()); + a = (-a) & ~sign_mask; negate = !negate; } + sign_mask = 0; + if (op_b_.size() != 8 * sizeof(unsigned long)) { + sign_mask = -1UL << op_b_.size(); + } if (op_b_.value(op_b_.size()-1)) { - b = (-b) & ~ (-1UL << op_b_.size()); + b = (-b) & ~sign_mask; negate = ! negate; } } + if (b == 0) { + vvp_vector4_t xval (wid_); + for (unsigned idx = 0 ; idx < wid_ ; idx += 1) + xval.set_bit(idx, BIT4_X); + + vvp_send_vec4(ptr.ptr()->out, xval, 0); + return; + } + unsigned long val = a / b; if (negate) val = -val; @@ -229,12 +258,23 @@ void vvp_arith_mod::wide_(vvp_net_ptr_t ptr) } vvp_vector2_t b2 (op_b_); - if (b2.is_NaN()) { + if (b2.is_NaN() || b2.is_zero()) { vvp_send_vec4(ptr.ptr()->out, x_val_, 0); return; } + bool negate = false; + if (signed_flag_) { + if (a2.value(a2.size()-1)) { + a2 = -a2; + negate = true; + } + if (b2.value(b2.size()-1)) { + b2 = -b2; + } + } vvp_vector2_t res = a2 % b2; + if (negate) res = -res; vvp_send_vec4(ptr.ptr()->out, vector2_to_vector4(res, res.size()), 0); } @@ -265,14 +305,21 @@ void vvp_arith_mod::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, the operands for now, and remember to put the sign back later. */ if (signed_flag_) { + unsigned long sign_mask = 0; + if (op_a_.size() != 8 * sizeof(unsigned long)) { + sign_mask = -1UL << op_a_.size(); + } if (op_a_.value(op_a_.size()-1)) { - a = (-a) & ~ (-1UL << op_a_.size()); + a = (-a) & ~sign_mask; negate = !negate; } + sign_mask = 0; + if (op_b_.size() != 8 * sizeof(unsigned long)) { + sign_mask = -1UL << op_b_.size(); + } if (op_b_.value(op_b_.size()-1)) { - b = (-b) & ~ (-1UL << op_b_.size()); - negate = ! negate; + b = (-b) & ~sign_mask; } } diff --git a/vvp/compile.cc b/vvp/compile.cc index 75a84311e..167c8d94a 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -979,7 +979,7 @@ void compile_arith_div_r(char*label, unsigned argc, struct symb_s*argv) make_arith(arith, label, argc, argv); } -void compile_arith_mod(char*label, long wid, +void compile_arith_mod(char*label, long wid, bool signed_flag, unsigned argc, struct symb_s*argv) { assert( wid > 0 ); @@ -990,7 +990,7 @@ void compile_arith_mod(char*label, long wid, return; } - vvp_arith_ *arith = new vvp_arith_mod(wid, false); + vvp_arith_ *arith = new vvp_arith_mod(wid, signed_flag); make_arith(arith, label, argc, argv); } diff --git a/vvp/compile.h b/vvp/compile.h index f6bb6a89e..8f99ebf72 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -157,7 +157,7 @@ extern void compile_arith_cast_real(char*label, 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); -extern void compile_arith_mod(char*label, long width, +extern void compile_arith_mod(char*label, long width, bool signed_flag, unsigned argc, struct symb_s*argv); extern void compile_arith_mult(char*label, long width, unsigned argc, struct symb_s*argv); diff --git a/vvp/lexor.lex b/vvp/lexor.lex index a2c19b7d5..362773f4f 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -93,6 +93,7 @@ ".arith/div.s" { return K_ARITH_DIV_S; } ".arith/mod" { return K_ARITH_MOD; } ".arith/mod.r" { return K_ARITH_MOD_R; } +".arith/mod.s" { return K_ARITH_MOD_S; } ".arith/mult" { return K_ARITH_MULT; } ".arith/mult.r" { return K_ARITH_MULT_R; } ".arith/pow" { return K_ARITH_POW; } diff --git a/vvp/parse.y b/vvp/parse.y index cd4081bb5..3a99ce994 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -67,7 +67,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_A K_ALIAS K_ALIAS_S K_ALIAS_R %token K_ARITH_ABS K_ARITH_DIV K_ARITH_DIV_R K_ARITH_DIV_S K_ARITH_MOD -%token K_ARITH_MOD_R +%token K_ARITH_MOD_R K_ARITH_MOD_S %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 K_ARITH_POW_S %token K_ARRAY K_ARRAY_I K_ARRAY_R K_ARRAY_S K_ARRAY_PORT @@ -288,7 +288,7 @@ statement | T_LABEL K_ARITH_MOD T_NUMBER ',' symbols ';' { struct symbv_s obj = $5; - compile_arith_mod($1, $3, obj.cnt, obj.vect); + compile_arith_mod($1, $3, false, obj.cnt, obj.vect); } | T_LABEL K_ARITH_MOD_R T_NUMBER ',' symbols ';' @@ -296,6 +296,11 @@ statement compile_arith_mod_r($1, obj.cnt, obj.vect); } + | T_LABEL K_ARITH_MOD_S T_NUMBER ',' symbols ';' + { struct symbv_s obj = $5; + compile_arith_mod($1, $3, true, obj.cnt, obj.vect); + } + | T_LABEL K_ARITH_MULT T_NUMBER ',' symbols ';' { struct symbv_s obj = $5; compile_arith_mult($1, $3, obj.cnt, obj.vect); diff --git a/vvp/vthread.cc b/vvp/vthread.cc index b8e6871f3..cab1c9c1d 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -3085,7 +3085,7 @@ bool of_LOADI_WR(vthread_t thr, vvp_code_t cp) static void do_verylong_mod(vthread_t thr, vvp_code_t cp, bool left_is_neg, bool right_is_neg) { - bool out_is_neg = left_is_neg != right_is_neg; + bool out_is_neg = left_is_neg; int len=cp->number; unsigned char *a, *z, *t; a = new unsigned char[len+1]; diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index 36cd82c49..3f8a3acf2 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -2020,6 +2020,21 @@ static void div_mod (vvp_vector2_t dividend, vvp_vector2_t divisor, remainder = vvp_vector2_t(dividend, mask.size()); } +vvp_vector2_t operator - (const vvp_vector2_t&that) +{ + vvp_vector2_t neg(that); + if (neg.wid_ == 0) return neg; + + const unsigned words = (neg.wid_ + neg.BITS_PER_WORD-1) / + neg.BITS_PER_WORD; + for (unsigned idx = 0 ; idx < words ; idx += 1) { + neg.vec_[idx] = ~neg.vec_[idx]; + } + neg += vvp_vector2_t(1, neg.wid_); + + return neg; +} + vvp_vector2_t operator / (const vvp_vector2_t÷nd, const vvp_vector2_t&divisor) { diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index 21d072ad2..e1bca9dec 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -564,6 +564,7 @@ class vvp_vector4array_aa : public vvp_vector4array_t, public automatic_hooks_s */ class vvp_vector2_t { + friend vvp_vector2_t operator - (const vvp_vector2_t&); friend vvp_vector2_t operator + (const vvp_vector2_t&, const vvp_vector2_t&); friend vvp_vector2_t operator * (const vvp_vector2_t&, @@ -619,6 +620,7 @@ extern bool operator >= (const vvp_vector2_t&, const vvp_vector2_t&); extern bool operator < (const vvp_vector2_t&, const vvp_vector2_t&); extern bool operator <= (const vvp_vector2_t&, const vvp_vector2_t&); extern bool operator == (const vvp_vector2_t&, const vvp_vector2_t&); +extern vvp_vector2_t operator - (const vvp_vector2_t&); extern vvp_vector2_t operator + (const vvp_vector2_t&, const vvp_vector2_t&); extern vvp_vector2_t operator * (const vvp_vector2_t&, const vvp_vector2_t&); extern vvp_vector2_t operator / (const vvp_vector2_t&, const vvp_vector2_t&);