Fix GitHub issue #244: handle mixed signed/unsigned power operations.

The signed version of the power operation in vvp should only be used
if the exponent is signed. Both signed and unsigned versions will
produce the correct result regardless of the type of the base operand,
provided it has been appropriately extended to the result size.

(cherry picked from commit ffb34861cf)
This commit is contained in:
Martin Whitaker 2019-05-11 21:33:29 +01:00
parent 27cac593e3
commit 9b5906b000
3 changed files with 17 additions and 14 deletions

View File

@ -388,7 +388,8 @@ NetNet* NetEBPow::synthesize(Design*des, NetScope*scope, NetExpr*root)
powr->set_line(*this);
des->add_node(powr);
powr->set_signed( has_sign() );
// The lpm_pwr object only cares about the signedness of the exponent.
powr->set_signed( right_->has_sign() );
connect(powr->pin_DataA(), lsig->pin(0));
connect(powr->pin_DataB(), rsig->pin(0));

View File

@ -1245,8 +1245,10 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net);
* width of a general power is the XXXX of the widths of the
* inputs.
*
* Power may be signed. If so, the output should be sign extended
* to fill in its result.
* Power may be signed. This indicates the type of the exponent. The
* base will always be treated as unsigned. The compiler must ensure
* the width of the base is equal to the width of the output to
* obtain the correct result when the base is really a signed value.
*
* - Part Select (IVL_LPM_PART_VP and IVL_LPM_PART_PV)
* There are two part select devices, one that extracts a part from a

View File

@ -117,13 +117,18 @@ static void draw_binary_vec4_arith(ivl_expr_t expr)
unsigned rwid = ivl_expr_width(re);
unsigned ewid = ivl_expr_width(expr);
int signed_flag = ivl_expr_signed(le) && ivl_expr_signed(re) ? 1 : 0;
int is_power_op = ivl_expr_opcode(expr) == 'p' ? 1 : 0;
/* The power operation differs from the other arithmetic operations
in that we only use the signed version of the operation if the
right hand operand (the exponent) is signed. */
int signed_flag = (ivl_expr_signed(le) || is_power_op) && ivl_expr_signed(re) ? 1 : 0;
const char*signed_string = signed_flag? "/s" : "";
/* All the arithmetic operations handled here require that the
operands (and the result) be the same width. We further
assume that the core has not given us an operand wider then
the expression width. So padd operands as needed. */
/* All the arithmetic operations handled here (except for the power
operation) require that the operands (and the result) be the same
width. We further assume that the core has not given us an operand
wider then the expression width. So pad operands as needed. */
draw_eval_vec4(le);
if (lwid != ewid) {
fprintf(vvp_out, " %%pad/%c %u;\n", ivl_expr_signed(le)? 's' : 'u', ewid);
@ -150,7 +155,7 @@ static void draw_binary_vec4_arith(ivl_expr_t expr)
}
draw_eval_vec4(re);
if (rwid != ewid) {
if ((rwid != ewid) && !is_power_op) {
fprintf(vvp_out, " %%pad/%c %u;\n", ivl_expr_signed(re)? 's' : 'u', ewid);
}
@ -171,11 +176,6 @@ static void draw_binary_vec4_arith(ivl_expr_t expr)
fprintf(vvp_out, " %%mod%s;\n", signed_string);
break;
case 'p':
/* Note that the power operator is signed if EITHER of
the operands is signed. This is different from other
arithmetic operators. */
if (ivl_expr_signed(le) || ivl_expr_signed(re))
signed_string = "/s";
fprintf(vvp_out, " %%pow%s;\n", signed_string);
break;