Fixed handling of power operator

This commit is contained in:
Clifford Wolf 2013-11-07 22:20:00 +01:00
parent d7cb62ac96
commit fc6dc0d7b8
4 changed files with 79 additions and 20 deletions

View File

@ -1070,6 +1070,23 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
return binop2rtlil(this, type_name, width, left, right); 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 // generate cells for binary operations: $lt, $le, $eq, $ne, $ge, $gt
if (0) { case AST_LT: type_name = "$lt"; } if (0) { case AST_LT: type_name = "$lt"; }
if (0) { case AST_LE: type_name = "$le"; } if (0) { case AST_LE: type_name = "$le"; }
@ -1088,19 +1105,18 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
return sig; 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_ADD: type_name = "$add"; }
if (0) { case AST_SUB: type_name = "$sub"; } if (0) { case AST_SUB: type_name = "$sub"; }
if (0) { case AST_MUL: type_name = "$mul"; } if (0) { case AST_MUL: type_name = "$mul"; }
if (0) { case AST_DIV: type_name = "$div"; } if (0) { case AST_DIV: type_name = "$div"; }
if (0) { case AST_MOD: type_name = "$mod"; } if (0) { case AST_MOD: type_name = "$mod"; }
if (0) { case AST_POW: type_name = "$pow"; }
{ {
if (width_hint < 0) if (width_hint < 0)
detectSignWidth(width_hint, sign_hint); detectSignWidth(width_hint, sign_hint);
RTLIL::SigSpec left = children[0]->genRTLIL(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); RTLIL::SigSpec right = children[1]->genRTLIL(width_hint, sign_hint);
int width = type == AST_POW ? left.width : std::max(left.width, right.width); int width = std::max(left.width, right.width);
if (width > width_hint && width_hint > 0) if (width > width_hint && width_hint > 0)
width = width_hint; width = width_hint;
if (width < width_hint) { if (width < width_hint) {
@ -1110,12 +1126,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
width = width_hint; width = width_hint;
if (type == AST_MUL) if (type == AST_MUL)
width = std::min(left.width + right.width, width_hint); 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; 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); return binop2rtlil(this, type_name, width, left, right);
} }

View 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_RIGHT: const_func = RTLIL::const_shr; }
if (0) { case AST_SHIFT_SLEFT: const_func = RTLIL::const_sshl; } 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_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) { 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 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); newNode = mkconst_bits(y.bits, sign_hint);
} }
break; break;
@ -1042,7 +1043,6 @@ skip_dynamic_range_lvalue_expansion:;
if (0) { case AST_MUL: const_func = RTLIL::const_mul; } if (0) { case AST_MUL: const_func = RTLIL::const_mul; }
if (0) { case AST_DIV: const_func = RTLIL::const_div; } if (0) { case AST_DIV: const_func = RTLIL::const_div; }
if (0) { case AST_MOD: const_func = RTLIL::const_mod; } 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) { 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 y = const_func(children[0]->bitsAsConst(width_hint, sign_hint),
children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint); children[1]->bitsAsConst(width_hint, sign_hint), sign_hint, sign_hint, width_hint);

View File

@ -17,6 +17,10 @@
* *
*/ */
// [[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/log.h"
#include "kernel/rtlil.h" #include "kernel/rtlil.h"
#include "libs/bigint/BigIntegerLibrary.hh" #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; int undef_bit_pos = -1;
log("--POW--\n");
BigInteger a = const2big(arg1, signed1, undef_bit_pos); BigInteger a = const2big(arg1, signed1, undef_bit_pos);
BigInteger b = const2big(arg2, signed2, undef_bit_pos); BigInteger b = const2big(arg2, signed2, undef_bit_pos);
BigInteger y = 1; BigInteger y = 1;
if (b < 0 || a == 0) { 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; y = 0;
} else { 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) { while (b > 0) {
y = y * a; if (b % 2 == 1)
if (y.getLength() > 0x10000) { y = (y * a) % modulus;
undef_bit_pos = 0; b = b / 2;
break; a = (a * a) % modulus;
}
b--;
} }
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)); return big2const(y, result_len >= 0 ? result_len : std::max(arg1.bits.size(), arg2.bits.size()), std::min(undef_bit_pos, 0));

15
tests/simple/constpower.v Normal file
View File

@ -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