Fix power operator on wide constants, bug761.

This commit is contained in:
Wilson Snyder 2017-06-05 20:30:01 -04:00
parent 50c4f60c68
commit 97093fdf81
8 changed files with 172 additions and 8 deletions

View File

@ -7,6 +7,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
*** Fix shifts by more than 32-bit numbers, bug1174. [Clifford Wolf]
*** Fix power operator on wide constants, bug761. [Clifford Wolf]
* Verilator 3.904 2017-05-30

View File

@ -282,6 +282,48 @@ WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, boo
}
}
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp) {
owp[0] = 1;
for (int i=1; i < VL_WORDS_I(obits); i++) owp[i] = 0;
// cppcheck-suppress variableScope
WData powstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
WData lastpowstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
WData lastoutstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here
// cppcheck-suppress variableScope
VL_ASSIGN_W(obits, powstore, lwp);
for (int bit=0; bit<rbits; bit++) {
if (bit>0) { // power = power*power
VL_ASSIGN_W(obits, lastpowstore, powstore);
VL_MUL_W(VL_WORDS_I(obits), powstore, lastpowstore, lastpowstore);
}
if (VL_BITISSET_W(rwp,bit)) { // out *= power
VL_ASSIGN_W(obits, lastoutstore, owp);
VL_MUL_W(VL_WORDS_I(obits), owp, lastoutstore, powstore);
}
}
return owp;
}
WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool lsign, bool rsign) {
if (rsign && VL_SIGN_W(rbits, rwp)) {
int words = VL_WORDS_I(obits);
VL_ZERO_W(obits, owp);
IData lor = 0; // 0=all zeros, ~0=all ones, else mix
for (int i=1; i < (words-1); ++i) {
lor |= lwp[i];
}
lor |= ( (lwp[words-1] == VL_MASK_I(rbits)) ? ~VL_UL(0) : 0);
if (lor==0 && lwp[0]==0) { return owp; } // "X" so return 0
else if (lor==0 && lwp[0]==1) { owp[0] = 1; return owp; } // 1
else if (lsign && lor == ~VL_UL(0) && lwp[0]==~VL_UL(0)) { // -1
if (rwp[0] & 1) { return VL_ALLONES_W(obits, owp); } // -1^odd=-1
else { owp[0] = 1; return owp; } // -1^even=1
}
return 0;
}
return VL_POW_WWW(obits, rbits, rbits, owp, lwp, rwp);
}
//===========================================================================
// Formatting

View File

@ -433,6 +433,7 @@ static inline IData VL_RTOIROUND_I_D(double lhs) { return ((vlsint32_t)(VL_ROUN
// (Requires clean input)
#define VL_SIGN_I(nbits,lhs) ((lhs) >> VL_BITBIT_I((nbits) - VL_UL(1)))
#define VL_SIGN_Q(nbits,lhs) ((lhs) >> VL_BITBIT_Q((nbits) - VL_ULL(1)))
#define VL_SIGN_W(nbits,rwp) ((rwp)[VL_BITWORD_I((nbits)-VL_UL(1))] >> VL_BITBIT_I((nbits)-VL_UL(1)))
#define VL_SIGNONES_I(nbits,lhs) (-(VL_SIGN_I(nbits,lhs)))
// Sign bit extended up to MSB, doesn't include unsigned portion
@ -513,6 +514,12 @@ static inline WDataOutP VL_ZERO_W(int obits, WDataOutP owp) {
for (int i=0; i < words; ++i) owp[i] = 0;
return owp;
}
static inline WDataOutP VL_ALLONES_W(int obits, WDataOutP owp) {
int words = VL_WORDS_I(obits);
for (int i=0; (i < (words-1)); ++i) owp[i] = ~VL_UL(0);
owp[words-1] = VL_MASK_I(obits);
return owp;
}
// EMIT_RULE: VL_ASSIGN: oclean=rclean; obits==lbits;
// For now, we always have a clean rhs.
@ -1208,6 +1215,7 @@ static inline QData VL_POW_QQQ(int, int, int rbits, QData lhs, QData rhs) {
}
return out;
}
WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp);
#define VL_POWSS_QQI(obits,lbits,rbits,lhs,rhs,lsign,rsign) VL_POWSS_QQQ(obits,lbits,rbits,lhs,rhs,lsign,rsign)
@ -1216,28 +1224,28 @@ static inline IData VL_POWSS_III(int obits, int, int rbits, IData lhs, IData rhs
if (rsign && VL_SIGN_I(rbits, rhs)) {
if (lhs==0) return 0; // "X"
else if (lhs==1) return 1;
else if (lsign && lhs==VL_MASK_I(obits)) { //-1
else if (lsign && lhs==VL_MASK_I(obits)) { // -1
if (rhs & 1) return VL_MASK_I(obits); // -1^odd=-1
else return 1; // -1^even=1
}
return 0;
}
return VL_POW_III(obits, obits, rbits, lhs, rhs);
return VL_POW_III(obits, rbits, rbits, lhs, rhs);
}
static inline QData VL_POWSS_QQQ(int obits, int, int rbits, QData lhs, QData rhs, bool lsign, bool rsign) {
if (VL_UNLIKELY(rhs==0)) return 1;
if (rsign && VL_SIGN_I(rbits, rhs)) {
if (lhs==0) return 0; // "X"
else if (lhs==1) return 1;
else if (lsign && lhs==VL_MASK_I(obits)) { //-1
else if (lsign && lhs==VL_MASK_I(obits)) { // -1
if (rhs & 1) return VL_MASK_I(obits); // -1^odd=-1
else return 1; // -1^even=1
}
return 0;
}
return VL_POW_QQQ(obits, obits, rbits, lhs, rhs);
return VL_POW_QQQ(obits, rbits, rbits, lhs, rhs);
}
WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP lwp, WDataInP rwp, bool lsign, bool rsign);
//===================================================================
// Concat/replication

View File

@ -509,6 +509,30 @@ public:
}
visit(nodep->castNodeBiop());
}
virtual void visit(AstPow* nodep) {
if (nodep->widthWords() > VL_MULS_MAX_WORDS) {
nodep->v3error("Unsupported: Power of "<<nodep->width()<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
}
visit(nodep->castNodeBiop());
}
virtual void visit(AstPowSS* nodep) {
if (nodep->widthWords() > VL_MULS_MAX_WORDS) {
nodep->v3error("Unsupported: Power of "<<nodep->width()<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
}
visit(nodep->castNodeBiop());
}
virtual void visit(AstPowSU* nodep) {
if (nodep->widthWords() > VL_MULS_MAX_WORDS) {
nodep->v3error("Unsupported: Power of "<<nodep->width()<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
}
visit(nodep->castNodeBiop());
}
virtual void visit(AstPowUS* nodep) {
if (nodep->widthWords() > VL_MULS_MAX_WORDS) {
nodep->v3error("Unsupported: Power of "<<nodep->width()<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
}
visit(nodep->castNodeBiop());
}
virtual void visit(AstCCast* nodep) {
// Extending a value of the same word width is just a NOP.
if (nodep->size()>VL_WORDSIZE) {

View File

@ -1518,8 +1518,6 @@ V3Number& V3Number::opPow (const V3Number& lhs, const V3Number& rhs, bool lsign,
if (lhs.isFourState() || rhs.isFourState()) return setAllBitsX();
if (rhs.isEqZero()) return setQuad(1); // Overrides lhs 0 -> return 0
// We may want to special case when the lhs is 2, so we can get larger outputs
if (lhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large >64bit ** power operator not implemented yet: "<<*this);
if (rhs.width()>64) m_fileline->v3fatalSrc("Unsupported: Large >64bit ** power operator not implemented yet: "<<*this);
if (rsign && rhs.isNegative()) {
if (lhs.isEqZero()) return setAllBitsXRemoved();
else if (lhs.isEqOne()) return setQuad(1);

View File

@ -803,7 +803,6 @@ private:
nodep->dtypeFrom(expDTypep);
// rhs already finalized in iterate_shift_prelim
iterateCheck(nodep,"LHS",nodep->lhsp(),SELF,FINAL,nodep->dtypep(),EXTEND_EXP);
if (nodep->width()>64) nodep->v3error("Unsupported: Large >64bit ** power operator not implemented.");
AstNode* newp = NULL; // No change
if (nodep->lhsp()->isSigned() && nodep->rhsp()->isSigned()) {
newp = new AstPowSS (nodep->fileline(), nodep->lhsp()->unlinkFrBack(),

18
test_regress/t/t_math_pow5.pl Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
compile (
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,73 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2004 by Wilson Snyder.
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
reg [67:0] q;
reg signed [67:0] qs;
initial begin
q = 68'he_12345678_9abcdef0 ** 68'h3;
if (q != 68'hcee3cb96ce96cf000) $stop;
//
q = 68'he_12345678_9abcdef0 ** 68'h5_6789abcd_ef012345;
if (q != 68'h0) $stop;
//
qs = 68'she_12345678_9abcdef0 ** 68'sh3;
if (qs != 68'shcee3cb96ce96cf000) $stop;
//
qs = 68'she_12345678_9abcdef0 ** 68'sh5_6789abcd_ef012345;
if (qs != 68'h0) $stop;
end
reg [67:0] left;
reg [67:0] right;
wire [67:0] outu = left ** right;
wire signed [67:0] outs = $signed(left) ** $signed(right);
integer cyc; initial cyc=1;
always @ (posedge clk) begin
if (cyc!=0) begin
cyc <= cyc + 1;
`ifdef TEST_VERBOSE
$write("%d %x %x %x %x\n", cyc, left, right, outu, outs);
`endif
if (cyc==1) begin
left <= 68'h1;
right <= '0;
end
if (cyc==2) begin
if (outu != 68'h1) $stop;
if (outs != 68'h1) $stop;
end
if (cyc==3) begin
left <= 68'he_12345678_9abcdef0;
right <= 68'h3;
end
if (cyc==4) begin
if (outu != 68'hcee3cb96ce96cf000) $stop;
if (outs != 68'hcee3cb96ce96cf000) $stop;
end
if (cyc==5) begin
left <= 68'he_12345678_9abcdef0;
right <= 68'h5_6789abcd_ef012345;
end
if (cyc==6) begin
if (outu != 68'h0) $stop;
if (outs != 68'h0) $stop;
end
if (cyc==9) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
end
endmodule