Fixed handling of power operator
authorClifford Wolf <clifford@clifford.at>
Thu, 7 Nov 2013 21:20:00 +0000 (22:20 +0100)
committerClifford Wolf <clifford@clifford.at>
Thu, 7 Nov 2013 21:20:00 +0000 (22:20 +0100)
frontends/ast/genrtlil.cc
frontends/ast/simplify.cc
kernel/calc.cc
tests/simple/constpower.v [new file with mode: 0644]

index 0c9c9be73b816d441195b50983abb8fccb758a80..2e8ab74927b99d5cde00fb745c7e461fbf5cacf7 100644 (file)
@@ -1070,6 +1070,23 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
                        return binop2rtlil(this, type_name, width, left, right);
                }
 
+       // generate cells for binary operations: $pow
+       case AST_POW:
+               {
+                       int right_width;
+                       bool right_signed;
+                       children[1]->detectSignWidth(right_width, right_signed);
+                       if (width_hint < 0)
+                               detectSignWidth(width_hint, sign_hint);
+                       RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
+                       RTLIL::SigSpec right = children[1]->genRTLIL(right_width, right_signed);
+                       int width = width_hint > 0 ? width_hint : left.width;
+                       is_signed = children[0]->is_signed;
+                       if (!flag_noopt && left.is_fully_const() && left.as_int() == 2 && !right_signed)
+                               return binop2rtlil(this, "$shl", width, RTLIL::SigSpec(1, left.width), right);
+                       return binop2rtlil(this, "$pow", width, left, right);
+               }
+
        // generate cells for binary operations: $lt, $le, $eq, $ne, $ge, $gt
        if (0) { case AST_LT: type_name = "$lt"; }
        if (0) { case AST_LE: type_name = "$le"; }
@@ -1088,19 +1105,18 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
                        return sig;
                }
 
-       // generate cells for binary operations: $add, $sub, $mul, $div, $mod, $pow
+       // generate cells for binary operations: $add, $sub, $mul, $div, $mod
        if (0) { case AST_ADD: type_name = "$add"; }
        if (0) { case AST_SUB: type_name = "$sub"; }
        if (0) { case AST_MUL: type_name = "$mul"; }
        if (0) { case AST_DIV: type_name = "$div"; }
        if (0) { case AST_MOD: type_name = "$mod"; }
-       if (0) { case AST_POW: type_name = "$pow"; }
                {
                        if (width_hint < 0)
                                detectSignWidth(width_hint, sign_hint);
                        RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
-                       RTLIL::SigSpec right = type == AST_POW ? children[1]->genRTLIL() : children[1]->genRTLIL(width_hint, sign_hint);
-                       int width = type == AST_POW ? left.width : std::max(left.width, right.width);
+                       RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint);
+                       int width = std::max(left.width, right.width);
                        if (width > width_hint && width_hint > 0)
                                width = width_hint;
                        if (width < width_hint) {
@@ -1110,12 +1126,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
                                        width = width_hint;
                                if (type == AST_MUL)
                                        width = std::min(left.width + right.width, width_hint);
-                               if (type == AST_POW)
-                                       width = width_hint;
                        }
                        is_signed = children[0]->is_signed && children[1]->is_signed;
-                       if (!flag_noopt && type == AST_POW && left.is_fully_const() && left.as_int() == 2)
-                               return binop2rtlil(this, "$shl", width, RTLIL::SigSpec(1, left.width), right);
                        return binop2rtlil(this, type_name, width, left, right);
                }
 
index 449ade4349858c40e3605915f758596376133b4b..5c0130d4281071a951c615b0fbc636fda55c51e2 100644 (file)
@@ -1017,9 +1017,10 @@ skip_dynamic_range_lvalue_expansion:;
                if (0) { case AST_SHIFT_RIGHT:  const_func = RTLIL::const_shr;  }
                if (0) { case AST_SHIFT_SLEFT:  const_func = RTLIL::const_sshl; }
                if (0) { case AST_SHIFT_SRIGHT: const_func = RTLIL::const_sshr; }
+               if (0) { case AST_POW:          const_func = RTLIL::const_pow; }
                        if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
                                RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
-                                               RTLIL::Const(children[1]->bits), sign_hint, false, width_hint);
+                                               RTLIL::Const(children[1]->bits), sign_hint, type == AST_POW ? sign_hint : false, width_hint);
                                newNode = mkconst_bits(y.bits, sign_hint);
                        }
                        break;
@@ -1042,7 +1043,6 @@ skip_dynamic_range_lvalue_expansion:;
                if (0) { case AST_MUL: const_func = RTLIL::const_mul; }
                if (0) { case AST_DIV: const_func = RTLIL::const_div; }
                if (0) { case AST_MOD: const_func = RTLIL::const_mod; }
-               if (0) { case AST_POW: const_func = RTLIL::const_pow; }
                        if (children[0]->type == AST_CONSTANT && children[1]->type == AST_CONSTANT) {
                                RTLIL::Const y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
                                                children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint);
index 605b1c13a0db74f2df993d8438b9b976ab100fb3..61a75c79f29d0dced90f00753b6227efa6f6d1e6 100644 (file)
  *
  */
 
+// [[CITE]] Power-Modulus Algorithm
+// Schneier, Bruce (1996). Applied Cryptography: Protocols, Algorithms, and Source Code in C,
+// Second Edition (2nd ed.). Wiley. ISBN 978-0-471-11709-4, page 244
+
 #include "kernel/log.h"
 #include "kernel/rtlil.h"
 #include "libs/bigint/BigIntegerLibrary.hh"
@@ -450,21 +454,49 @@ RTLIL::Const RTLIL::const_pow(const RTLIL::Const &arg1, const RTLIL::Const &arg2
 {
        int undef_bit_pos = -1;
 
+       log("--POW--\n");
        BigInteger a = const2big(arg1, signed1, undef_bit_pos);
        BigInteger b = const2big(arg2, signed2, undef_bit_pos);
        BigInteger y = 1;
 
-       if (b < 0 || a == 0) {
-               y = 0;
-       } else { 
+       if (a == 0 && b < 0)
+               return RTLIL::Const(RTLIL::State::Sx, result_len);
+
+       if (a == 0 && b > 0)
+               return RTLIL::Const(RTLIL::State::S0, result_len);
+
+       if (b < 0)
+       {
+               if (a < -1 || a > 1)
+                       y = 0;
+               if (a == -1)
+                       y = (-b % 2) == 0 ? 1 : -1;
+       }
+
+       if (b > 0)
+       {
+               // Power-modulo with 2^result_len as modulus
+               BigInteger modulus = 1;
+               int modulus_bits = (result_len >= 0 ? result_len : 1024);
+               for (int i = 0; i < modulus_bits; i++)
+                       modulus *= 2;
+
+               bool flip_result_sign = false;
+               if (a < 0) {
+                       a *= -1;
+                       if (b % 2 == 1)
+                               flip_result_sign = true;
+               }
+
                while (b > 0) {
-                       y = y * a;
-                       if (y.getLength() > 0x10000) {
-                               undef_bit_pos = 0;
-                               break;
-                       }
-                       b--;
+                       if (b % 2 == 1)
+                               y = (y * a) % modulus;
+                       b = b / 2;
+                       a = (a * a) % modulus;
                }
+
+               if (flip_result_sign)
+                       y *= -1;
        }
 
        return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0));
diff --git a/tests/simple/constpower.v b/tests/simple/constpower.v
new file mode 100644 (file)
index 0000000..451866a
--- /dev/null
@@ -0,0 +1,15 @@
+module constpower(ys, yu);
+
+output [8*8*8-1:0] ys, yu;
+
+genvar i, j;
+
+generate
+       for (i = 0; i < 8; i = i+1)
+       for (j = 0; j < 8; j = j+1) begin:V
+               assign ys[i*8 + j*64 + 7 : i*8 + j*64] = $signed(i-4) ** $signed(j-4);
+               assign yu[i*8 + j*64 + 7 : i*8 + j*64] = $unsigned(i) ** $unsigned(j);
+       end
+endgenerate
+
+endmodule