Support $countbits (#2287)

This commit is contained in:
Yossi Nivin 2020-05-10 20:27:22 +02:00 committed by GitHub
parent 070bcddf5a
commit f9a0cf0cff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 441 additions and 9 deletions

View File

@ -5,6 +5,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 4.035 devel * Verilator 4.035 devel
**** Support $countbits. (#2287) [Yossi Nivin]
**** Support $isunbounded and parameter $. (#2104) **** Support $isunbounded and parameter $. (#2104)
**** Support unpacked array .sum and .product. **** Support unpacked array .sum and .product.

View File

@ -3104,11 +3104,11 @@ uwire keyword.
=head2 SystemVerilog 2005 (IEEE 1800-2005) Support =head2 SystemVerilog 2005 (IEEE 1800-2005) Support
Verilator supports ==? and !=? operators, ++ and -- in some contexts, Verilator supports ==? and !=? operators, ++ and -- in some contexts,
$bits, $countones, $error, $fatal, $info, $isunknown, $onehot, $onehot0, $bits, $countbits, $countones, $error, $fatal, $info, $isunknown, $onehot,
$unit, $warning, always_comb, always_ff, always_latch, bit, byte, chandle, $onehot0, $unit, $warning, always_comb, always_ff, always_latch, bit, byte,
const, do-while, enum, export, final, import, int, interface, logic, chandle, const, do-while, enum, export, final, import, int, interface,
longint, modport, package, program, shortint, struct, time, typedef, union, logic, longint, modport, package, program, shortint, struct, time, typedef,
var, void, priority case/if, and unique case/if. union, var, void, priority case/if, and unique case/if.
It also supports .name and .* interconnection. It also supports .name and .* interconnection.
@ -3935,9 +3935,9 @@ All timing control statements are ignored.
Verilator does not perform warning checking on uwires, it treats the uwire Verilator does not perform warning checking on uwires, it treats the uwire
keyword as if it were the normal wire keyword. keyword as if it were the normal wire keyword.
=item $bits, $countones, $error, $fatal, $finish, $info, $isunknown, =item $bits, $countbits, $countones, $error, $fatal, $finish, $info,
$onehot, $onehot0, $readmemb, $readmemh, $signed, $stime, $stop, $time, $isunknown, $onehot, $onehot0, $readmemb, $readmemh, $signed, $stime, $stop,
$unsigned, $warning. $time, $unsigned, $warning.
Generally supported. Generally supported.

View File

@ -47,5 +47,6 @@ Tobias Wölfel
Todd Strader Todd Strader
Veripool API Bot Veripool API Bot
Wilson Snyder Wilson Snyder
Yossi Nivin
Yutetsu TAKATSUKASA Yutetsu TAKATSUKASA
Yves Mathieu Yves Mathieu

View File

@ -1217,6 +1217,36 @@ static inline IData VL_COUNTONES_W(int words, WDataInP lwp) VL_MT_SAFE {
return r; return r;
} }
// EMIT_RULE: VL_COUNTBITS_II: oclean = false; lhs clean
static inline IData VL_COUNTBITS_I(int lbits, IData lhs, IData ctrl0, IData ctrl1,
IData ctrl2) VL_PURE {
int ctrlSum = (ctrl0 & 0x1) + (ctrl1 & 0x1) + (ctrl2 & 0x1);
if (ctrlSum == 3) {
return VL_COUNTONES_I(lhs);
} else if (ctrlSum == 0) {
IData mask = (lbits == 32) ? -1 : ((1 << lbits) - 1);
return VL_COUNTONES_I(~lhs & mask);
} else {
return (lbits == 32) ? 32 : lbits;
}
}
static inline IData VL_COUNTBITS_Q(int lbits, QData lhs, IData ctrl0, IData ctrl1,
IData ctrl2) VL_PURE {
return VL_COUNTBITS_I(32, static_cast<IData>(lhs), ctrl0, ctrl1, ctrl2)
+ VL_COUNTBITS_I(lbits - 32, static_cast<IData>(lhs >> 32), ctrl0, ctrl1, ctrl2);
}
#define VL_COUNTBITS_E VL_COUNTBITS_I
static inline IData VL_COUNTBITS_W(int lbits, int words, WDataInP lwp, IData ctrl0, IData ctrl1,
IData ctrl2) VL_MT_SAFE {
EData r = 0;
IData wordLbits = 32;
for (int i = 0; i < words; ++i) {
if (i == words - 1) { wordLbits = lbits % 32; }
r += VL_COUNTBITS_E(wordLbits, lwp[i], ctrl0, ctrl1, ctrl2);
}
return r;
}
static inline IData VL_ONEHOT_I(IData lhs) VL_PURE { static inline IData VL_ONEHOT_I(IData lhs) VL_PURE {
return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); return (((lhs & (lhs - 1)) == 0) & (lhs != 0));
} }

View File

@ -1999,6 +1999,43 @@ public:
virtual bool same(const AstNode*) const { return true; } virtual bool same(const AstNode*) const { return true; }
}; };
class AstNodeQuadop : public AstNodeMath {
// Quaternary math
public:
AstNodeQuadop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths, AstNode* fhs)
: AstNodeMath(t, fl) {
setOp1p(lhs);
setOp2p(rhs);
setOp3p(ths);
setOp4p(fhs);
}
ASTNODE_BASE_FUNCS(NodeQuadop)
AstNode* lhsp() const { return op1p(); }
AstNode* rhsp() const { return op2p(); }
AstNode* thsp() const { return op3p(); }
AstNode* fhsp() const { return op4p(); }
void lhsp(AstNode* nodep) { return setOp1p(nodep); }
void rhsp(AstNode* nodep) { return setOp2p(nodep); }
void thsp(AstNode* nodep) { return setOp3p(nodep); }
void fhsp(AstNode* nodep) { return setOp4p(nodep); }
// METHODS
// Set out to evaluation of a AstConst'ed
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs,
const V3Number& ths, const V3Number& fhs)
= 0;
virtual bool cleanLhs() const = 0; // True if LHS must have extra upper bits zero
virtual bool cleanRhs() const = 0; // True if RHS must have extra upper bits zero
virtual bool cleanThs() const = 0; // True if THS must have extra upper bits zero
virtual bool cleanFhs() const = 0; // True if THS must have extra upper bits zero
virtual bool sizeMattersLhs() const = 0; // True if output result depends on lhs size
virtual bool sizeMattersRhs() const = 0; // True if output result depends on rhs size
virtual bool sizeMattersThs() const = 0; // True if output result depends on ths size
virtual bool sizeMattersFhs() const = 0; // True if output result depends on ths size
virtual int instrCount() const { return widthInstrs(); }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode*) const { return true; }
};
class AstNodeBiCom : public AstNodeBiop { class AstNodeBiCom : public AstNodeBiop {
// Binary math with commutative properties // Binary math with commutative properties
public: public:

View File

@ -5424,6 +5424,33 @@ public:
virtual bool sizeMattersLhs() const { return false; } virtual bool sizeMattersLhs() const { return false; }
virtual int instrCount() const { return widthInstrs() * 16; } virtual int instrCount() const { return widthInstrs() * 16; }
}; };
class AstCountBits : public AstNodeQuadop {
// Number of bits set in vector
public:
AstCountBits(FileLine* fl, AstNode* exprp, AstNode* ctrl1p)
: ASTGEN_SUPER(fl, exprp, ctrl1p, ctrl1p->cloneTree(false), ctrl1p->cloneTree(false)) {}
AstCountBits(FileLine* fl, AstNode* exprp, AstNode* ctrl1p, AstNode* ctrl2p)
: ASTGEN_SUPER(fl, exprp, ctrl1p, ctrl2p, ctrl2p->cloneTree(false)) {}
AstCountBits(FileLine* fl, AstNode* exprp, AstNode* ctrl1p, AstNode* ctrl2p, AstNode* ctrl3p)
: ASTGEN_SUPER(fl, exprp, ctrl1p, ctrl2p, ctrl3p) {}
ASTNODE_NODE_FUNCS(CountBits)
virtual void numberOperate(V3Number& out, const V3Number& expr, const V3Number& ctrl1,
const V3Number& ctrl2, const V3Number& ctrl3) {
out.opCountBits(expr, ctrl1, ctrl2, ctrl3);
}
virtual string emitVerilog() { return "%f$countbits(%l, %r, %f, %o)"; }
virtual string emitC() { return ""; }
virtual bool cleanOut() const { return false; }
virtual bool cleanLhs() const { return true; }
virtual bool cleanRhs() const { return true; }
virtual bool cleanThs() const { return true; }
virtual bool cleanFhs() const { return true; }
virtual bool sizeMattersLhs() const { return false; }
virtual bool sizeMattersRhs() const { return false; }
virtual bool sizeMattersThs() const { return false; }
virtual bool sizeMattersFhs() const { return false; }
virtual int instrCount() const { return widthInstrs() * 16; }
};
class AstCountOnes : public AstNodeUniop { class AstCountOnes : public AstNodeUniop {
// Number of bits set in vector // Number of bits set in vector
public: public:

View File

@ -128,6 +128,15 @@ private:
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp()); if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
if (nodep->sizeMattersThs()) ensureCast(nodep->thsp()); if (nodep->sizeMattersThs()) ensureCast(nodep->thsp());
} }
virtual void visit(AstNodeQuadop* nodep) VL_OVERRIDE {
iterateChildren(nodep);
nodep->user1(nodep->lhsp()->user1() | nodep->rhsp()->user1() | nodep->thsp()->user1()
| nodep->fhsp()->user1());
if (nodep->sizeMattersLhs()) ensureCast(nodep->lhsp());
if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp());
if (nodep->sizeMattersThs()) ensureCast(nodep->thsp());
if (nodep->sizeMattersFhs()) ensureCast(nodep->fhsp());
}
virtual void visit(AstCCast* nodep) VL_OVERRIDE { virtual void visit(AstCCast* nodep) VL_OVERRIDE {
iterateChildren(nodep); iterateChildren(nodep);
ensureLower32Cast(nodep); ensureLower32Cast(nodep);

View File

@ -160,6 +160,15 @@ private:
if (nodep->cleanThs()) ensureClean(nodep->thsp()); if (nodep->cleanThs()) ensureClean(nodep->thsp());
// no setClean.. must do it in each user routine. // no setClean.. must do it in each user routine.
} }
void operandQuadop(AstNodeQuadop* nodep) {
iterateChildren(nodep);
computeCppWidth(nodep);
if (nodep->cleanLhs()) { ensureClean(nodep->lhsp()); }
if (nodep->cleanRhs()) { ensureClean(nodep->rhsp()); }
if (nodep->cleanThs()) { ensureClean(nodep->thsp()); }
if (nodep->cleanFhs()) { ensureClean(nodep->fhsp()); }
// no setClean.. must do it in each user routine.
}
// VISITORS // VISITORS
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE { virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
@ -192,6 +201,10 @@ private:
operandBiop(nodep); operandBiop(nodep);
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp())); setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
} }
virtual void visit(AstNodeQuadop* nodep) VL_OVERRIDE {
operandQuadop(nodep);
setClean(nodep, nodep->cleanOut());
}
virtual void visit(AstNodeMath* nodep) VL_OVERRIDE { virtual void visit(AstNodeMath* nodep) VL_OVERRIDE {
iterateChildren(nodep); iterateChildren(nodep);
computeCppWidth(nodep); computeCppWidth(nodep);

View File

@ -664,6 +664,14 @@ private:
UINFO(4, "TRICONST -> " << num << endl); UINFO(4, "TRICONST -> " << num << endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep); VL_DO_DANGLING(replaceNum(nodep, num), nodep);
} }
void replaceConst(AstNodeQuadop* nodep) {
V3Number num(nodep, nodep->width());
nodep->numberOperate(
num, VN_CAST(nodep->lhsp(), Const)->num(), VN_CAST(nodep->rhsp(), Const)->num(),
VN_CAST(nodep->thsp(), Const)->num(), VN_CAST(nodep->fhsp(), Const)->num());
UINFO(4, "QUADCONST -> " << num << endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
void replaceConstString(AstNode* oldp, const string& num) { void replaceConstString(AstNode* oldp, const string& num) {
// Replace oldp node with a constant set to specified value // Replace oldp node with a constant set to specified value
@ -2258,6 +2266,7 @@ private:
// Generic constants on both side. Do this first to avoid other replacements // Generic constants on both side. Do this first to avoid other replacements
TREEOPC("AstNodeBiop {$lhsp.castConst, $rhsp.castConst}", "replaceConst(nodep)"); TREEOPC("AstNodeBiop {$lhsp.castConst, $rhsp.castConst}", "replaceConst(nodep)");
TREEOPC("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque()}", "replaceConst(nodep)"); TREEOPC("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque()}", "replaceConst(nodep)");
TREEOPC("AstNodeQuadop{$lhsp.castConst, $rhsp.castConst, $thsp.castConst, $fhsp.castConst}", "replaceConst(nodep)");
// Zero on one side or the other // Zero on one side or the other
TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)"); TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
TREEOP ("AstAnd {$lhsp.isZero, $rhsp, isTPure($rhsp)}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure TREEOP ("AstAnd {$lhsp.isZero, $rhsp, isTPure($rhsp)}", "replaceZero(nodep)"); // Can't use replaceZeroChkPure as we make this pattern in ChkPure

View File

@ -1000,6 +1000,26 @@ public:
emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)", nodep->lhsp(), emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)", nodep->lhsp(),
nodep->rhsp(), NULL); nodep->rhsp(), NULL);
} }
virtual void visit(AstCountBits* nodep) {
putbs("VL_COUNTBITS_");
emitIQW(nodep->lhsp());
puts("(");
puts(cvtToStr(nodep->lhsp()->widthMin()));
puts(", ");
if (nodep->lhsp()->isWide()) {
puts(cvtToStr(nodep->lhsp()->widthWords())); // Note argument width, not node width
// (which is always 32)
puts(", ");
}
iterateAndNextNull(nodep->lhsp());
puts(", ");
iterateAndNextNull(nodep->rhsp());
puts(", ");
iterateAndNextNull(nodep->thsp());
puts(", ");
iterateAndNextNull(nodep->fhsp());
puts(")");
}
// Terminals // Terminals
virtual void visit(AstVarRef* nodep) VL_OVERRIDE { virtual void visit(AstVarRef* nodep) VL_OVERRIDE {
puts(nodep->hiernameProtect()); puts(nodep->hiernameProtect());

View File

@ -411,13 +411,15 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
// Operators // Operators
virtual void emitVerilogFormat(AstNode* nodep, const string& format, AstNode* lhsp = NULL, virtual void emitVerilogFormat(AstNode* nodep, const string& format, AstNode* lhsp = NULL,
AstNode* rhsp = NULL, AstNode* thsp = NULL) { AstNode* rhsp = NULL, AstNode* thsp = NULL,
AstNode* fhsp = NULL) {
// Look at emitVerilog() format for term/uni/dual/triops, // Look at emitVerilog() format for term/uni/dual/triops,
// and write out appropriate text. // and write out appropriate text.
// %f Potential fileline-if-change and line break // %f Potential fileline-if-change and line break
// %l lhsp - if appropriate // %l lhsp - if appropriate
// %r rhsp - if appropriate // %r rhsp - if appropriate
// %t thsp - if appropriate // %t thsp - if appropriate
// %o fhsp - if appropriate
// %d dtypep - if appropriate // %d dtypep - if appropriate
// %k Potential line break // %k Potential line break
bool inPct = false; bool inPct = false;
@ -450,6 +452,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
iterateAndNextNull(thsp); iterateAndNextNull(thsp);
break; break;
} }
case 'o': {
UASSERT_OBJ(thsp, nodep, "emitVerilog() references undef node");
iterateAndNextNull(fhsp);
break;
}
case 'd': { case 'd': {
UASSERT_OBJ(nodep->dtypep(), nodep, "emitVerilog() references undef node"); UASSERT_OBJ(nodep->dtypep(), nodep, "emitVerilog() references undef node");
iterateAndNextNull(nodep->dtypep()); iterateAndNextNull(nodep->dtypep());

View File

@ -38,6 +38,9 @@
#define NUM_ASSERT_OP_ARGS3(arg1, arg2, arg3) \ #define NUM_ASSERT_OP_ARGS3(arg1, arg2, arg3) \
UASSERT((this != &(arg1) && this != &(arg2) && this != &(arg3)), \ UASSERT((this != &(arg1) && this != &(arg2) && this != &(arg3)), \
"Number operation called with same source and dest"); "Number operation called with same source and dest");
#define NUM_ASSERT_OP_ARGS4(arg1, arg2, arg3, arg4) \
UASSERT((this != &(arg1) && this != &(arg2) && this != &(arg3) && this != &(arg4)), \
"Number operation called with same source and dest");
#define NUM_ASSERT_LOGIC_ARGS1(arg1) \ #define NUM_ASSERT_LOGIC_ARGS1(arg1) \
UASSERT((!(arg1).isDouble() && !(arg1).isString()), \ UASSERT((!(arg1).isDouble() && !(arg1).isString()), \
@ -47,6 +50,12 @@
NUM_ASSERT_LOGIC_ARGS1(arg1); \ NUM_ASSERT_LOGIC_ARGS1(arg1); \
NUM_ASSERT_LOGIC_ARGS1(arg2); NUM_ASSERT_LOGIC_ARGS1(arg2);
#define NUM_ASSERT_LOGIC_ARGS4(arg1, arg2, arg3, arg4) \
NUM_ASSERT_LOGIC_ARGS1(arg1); \
NUM_ASSERT_LOGIC_ARGS1(arg2); \
NUM_ASSERT_LOGIC_ARGS1(arg3); \
NUM_ASSERT_LOGIC_ARGS1(arg4);
#define NUM_ASSERT_STRING_ARGS1(arg1) \ #define NUM_ASSERT_STRING_ARGS1(arg1) \
UASSERT((arg1).isString(), \ UASSERT((arg1).isString(), \
"Number operation called with non-string argument: '" << (arg1) << '"'); "Number operation called with non-string argument: '" << (arg1) << '"');
@ -953,6 +962,37 @@ int V3Number::widthMin() const {
return 1; // one bit even if number is == 0 return 1; // one bit even if number is == 0
} }
uint32_t V3Number::countBits(const V3Number& ctrl) const {
int n = 0;
for (int bit = 0; bit < this->width(); ++bit) {
switch (ctrl.bitIs(0)) {
case '0':
if (bitIs0(bit)) ++n;
break;
case '1':
if (bitIs1(bit)) ++n;
break;
case 'x':
if (bitIsX(bit)) ++n;
break;
case 'z':
if (bitIsZ(bit)) ++n;
break;
}
}
return n;
}
uint32_t V3Number::countBits(const V3Number& ctrl1, const V3Number& ctrl2,
const V3Number& ctrl3) const {
int n = countBits(ctrl1);
if (ctrl2.bitIs(0) != ctrl1.bitIs(0)) n += countBits(ctrl2);
if ((ctrl3.bitIs(0) != ctrl1.bitIs(0)) && (ctrl3.bitIs(0) != ctrl2.bitIs(0))) {
n += countBits(ctrl3);
}
return n;
}
uint32_t V3Number::countOnes() const { uint32_t V3Number::countOnes() const {
int n = 0; int n = 0;
for (int bit = 0; bit < this->width(); bit++) { for (int bit = 0; bit < this->width(); bit++) {
@ -1095,6 +1135,15 @@ V3Number& V3Number::opRedXnor(const V3Number& lhs) {
return setSingleBits(outc); return setSingleBits(outc);
} }
V3Number& V3Number::opCountBits(const V3Number& expr, const V3Number& ctrl1, const V3Number& ctrl2,
const V3Number& ctrl3) {
NUM_ASSERT_OP_ARGS4(expr, ctrl1, ctrl2, ctrl3);
NUM_ASSERT_LOGIC_ARGS4(expr, ctrl1, ctrl2, ctrl3);
setZero();
m_value[0] = expr.countBits(ctrl1, ctrl2, ctrl3);
opCleanThis();
return *this;
}
V3Number& V3Number::opCountOnes(const V3Number& lhs) { V3Number& V3Number::opCountOnes(const V3Number& lhs) {
NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_LOGIC_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs);

View File

@ -284,6 +284,8 @@ public:
uint32_t toHash() const; uint32_t toHash() const;
uint32_t edataWord(int eword) const; uint32_t edataWord(int eword) const;
uint8_t dataByte(int byte) const; uint8_t dataByte(int byte) const;
uint32_t countBits(const V3Number& ctrl) const;
uint32_t countBits(const V3Number& ctrl1, const V3Number& ctrl2, const V3Number& ctrl3) const;
uint32_t countOnes() const; uint32_t countOnes() const;
uint32_t uint32_t
mostSetBitP1() const; // Highest bit set plus one, IE for 16 return 5, for 0 return 0. mostSetBitP1() const; // Highest bit set plus one, IE for 16 return 5, for 0 return 0.
@ -314,6 +316,8 @@ public:
V3Number& opRedAnd(const V3Number& lhs); V3Number& opRedAnd(const V3Number& lhs);
V3Number& opRedXor(const V3Number& lhs); V3Number& opRedXor(const V3Number& lhs);
V3Number& opRedXnor(const V3Number& lhs); V3Number& opRedXnor(const V3Number& lhs);
V3Number& opCountBits(const V3Number& expr, const V3Number& ctrl1, const V3Number& ctrl2,
const V3Number& ctrl3);
V3Number& opCountOnes(const V3Number& lhs); V3Number& opCountOnes(const V3Number& lhs);
V3Number& opIsUnknown(const V3Number& lhs); V3Number& opIsUnknown(const V3Number& lhs);
V3Number& opOneHot(const V3Number& lhs); V3Number& opOneHot(const V3Number& lhs);

View File

@ -539,6 +539,17 @@ private:
fetchConst(nodep->thsp())->num()); fetchConst(nodep->thsp())->num());
} }
} }
virtual void visit(AstNodeQuadop* nodep) VL_OVERRIDE {
if (!optimizable()) return; // Accelerate
checkNodeInfo(nodep);
iterateChildren(nodep);
if (!m_checkOnly && optimizable()) {
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(),
fetchConst(nodep->rhsp())->num(),
fetchConst(nodep->thsp())->num(),
fetchConst(nodep->fhsp())->num());
}
}
virtual void visit(AstLogAnd* nodep) VL_OVERRIDE { virtual void visit(AstLogAnd* nodep) VL_OVERRIDE {
// Need to short circuit // Need to short circuit
if (!optimizable()) return; // Accelerate if (!optimizable()) return; // Accelerate

View File

@ -1140,6 +1140,18 @@ private:
userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
userIterateAndNext(nodep->rhsp(), WidthVP(SELF, BOTH).p()); userIterateAndNext(nodep->rhsp(), WidthVP(SELF, BOTH).p());
} }
virtual void visit(AstCountBits* nodep) VL_OVERRIDE {
if (m_vup->prelim()) {
iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);
iterateCheckSizedSelf(nodep, "RHS", nodep->rhsp(), SELF, BOTH);
iterateCheckSizedSelf(nodep, "THS", nodep->thsp(), SELF, BOTH);
iterateCheckSizedSelf(nodep, "FHS", nodep->fhsp(), SELF, BOTH);
// If it's a 32 bit number, we need a 6 bit number as we need to return '32'.
int selwidth = V3Number::log2b(nodep->lhsp()->width()) + 1;
nodep->dtypeSetLogicSized(selwidth,
VSigning::UNSIGNED); // Spec doesn't indicate if an integer
}
}
virtual void visit(AstCountOnes* nodep) VL_OVERRIDE { virtual void visit(AstCountOnes* nodep) VL_OVERRIDE {
if (m_vup->prelim()) { if (m_vup->prelim()) {
iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH);

View File

@ -434,6 +434,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
<S05,S09,S12,S17,SAX>{ <S05,S09,S12,S17,SAX>{
/* System Tasks */ /* System Tasks */
"$bits" { FL; return yD_BITS; } "$bits" { FL; return yD_BITS; }
"$countbits" { FL; return yD_COUNTBITS; }
"$countones" { FL; return yD_COUNTONES; } "$countones" { FL; return yD_COUNTONES; }
"$dimensions" { FL; return yD_DIMENSIONS; } "$dimensions" { FL; return yD_DIMENSIONS; }
"$error" { FL; return yD_ERROR; } "$error" { FL; return yD_ERROR; }

View File

@ -552,6 +552,7 @@ class AstSenTree;
%token<fl> yD_CLOG2 "$clog2" %token<fl> yD_CLOG2 "$clog2"
%token<fl> yD_COS "$cos" %token<fl> yD_COS "$cos"
%token<fl> yD_COSH "$cosh" %token<fl> yD_COSH "$cosh"
%token<fl> yD_COUNTBITS "$countbits"
%token<fl> yD_COUNTONES "$countones" %token<fl> yD_COUNTONES "$countones"
%token<fl> yD_DIMENSIONS "$dimensions" %token<fl> yD_DIMENSIONS "$dimensions"
%token<fl> yD_DISPLAY "$display" %token<fl> yD_DISPLAY "$display"
@ -3391,6 +3392,11 @@ system_f_call_or_t<nodep>: // IEEE: part of system_tf_call (can be task or func)
| yD_CLOG2 '(' expr ')' { $$ = new AstCLog2($1,$3); } | yD_CLOG2 '(' expr ')' { $$ = new AstCLog2($1,$3); }
| yD_COS '(' expr ')' { $$ = new AstCosD($1,$3); } | yD_COS '(' expr ')' { $$ = new AstCosD($1,$3); }
| yD_COSH '(' expr ')' { $$ = new AstCoshD($1,$3); } | yD_COSH '(' expr ')' { $$ = new AstCoshD($1,$3); }
| yD_COUNTBITS '(' expr ',' expr ')' { $$ = new AstCountBits($1,$3,$5); }
| yD_COUNTBITS '(' expr ',' expr ',' expr ')' { $$ = new AstCountBits($1,$3,$5,$7); }
| yD_COUNTBITS '(' expr ',' expr ',' expr ',' expr ')' { $$ = new AstCountBits($1,$3,$5,$7,$9); }
| yD_COUNTBITS '(' expr ',' expr ',' expr ',' expr ',' exprList ')'
{$11->v3error("Unsupported: $countbits with more than 3 control fields"); }
| yD_COUNTONES '(' expr ')' { $$ = new AstCountOnes($1,$3); } | yD_COUNTONES '(' expr ')' { $$ = new AstCountOnes($1,$3); }
| yD_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_DIMENSIONS,$3); } | yD_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_DIMENSIONS,$3); }
| yD_EXP '(' expr ')' { $$ = new AstExpD($1,$3); } | yD_EXP '(' expr ')' { $$ = new AstExpD($1,$3); }

View File

@ -0,0 +1,21 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2019 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,134 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 Yossi Nivin.
// SPDX-License-Identifier: CC0-1.0
module t(/*AUTOARG*/
// Inputs
clk
);
input clk;
reg [15:0] in16;
reg [31:0] in32;
reg [63:0] in64;
// Non-standard size
reg [9:0] in10;
reg [20:0] in21;
reg [58:0] in59;
reg [69:0] in70;
reg [31:0] ctrl0;
reg [31:0] ctrl1;
reg [31:0] ctrl2;
reg [4:0] result_16_1;
reg [4:0] result_16_2;
reg [4:0] result_16_3;
reg [5:0] result_32_1;
reg [5:0] result_32_2;
reg [5:0] result_32_3;
reg [6:0] result_64_1;
reg [6:0] result_64_2;
reg [6:0] result_64_3;
reg [3:0] result_10_3;
reg [4:0] result_21_3;
reg [5:0] result_59_3;
reg [6:0] result_70_3;
always @* begin
result_16_1 = $countbits(in16, ctrl0);
result_16_2 = $countbits(in16, ctrl0, ctrl1);
result_16_3 = $countbits(in16, ctrl0, ctrl1, ctrl2);
result_32_1 = $countbits(in32, ctrl0);
result_32_2 = $countbits(in32, ctrl0, ctrl1);
result_32_3 = $countbits(in32, ctrl0, ctrl1, ctrl2);
result_64_1 = $countbits(in64, ctrl0);
result_64_2 = $countbits(in64, ctrl0, ctrl1);
result_64_3 = $countbits(in64, ctrl0, ctrl1, ctrl2);
result_10_3 = $countbits(in10, ctrl0, ctrl1, ctrl2);
result_21_3 = $countbits(in21, ctrl0, ctrl1, ctrl2);
result_59_3 = $countbits(in59, ctrl0, ctrl1, ctrl2);
result_70_3 = $countbits(in70, ctrl0, ctrl1, ctrl2);
end
integer cyc=0;
// Test loop
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc == 0) begin
// Constants
if ($countbits(32'b11001011101, '1) != 7) $stop;
if ($countbits(32'b11001011101, '1, 'z) != 7) $stop;
if ($countbits(32'b11001011101, '1, '0) != 32) $stop;
if ($countbits(20'b11001011101, '1, '0) != 20) $stop;
if ($countbits(20'b1100x01z101, '1, '0) != 18) $stop;
if ($countbits(20'b1100x01z101, 2, 2'bx1) != 18) $stop;
if ($countbits(32'b1100x01z101, 'x, 'z) != 2) $stop;
if ($countbits(32'b1100x01z101, 'x, 'z, '1) != 7) $stop;
end
else if (cyc == 1) begin
in16 <= 16'h0AF0;
in32 <= 32'hA0F300;
in64 <= 64'hA5A5A5A5A5A5A5A5;
in10 <= 10'b1010_1011;
in21 <= 21'h10F102;
in59 <= 59'h7050137210;
in70 <= 70'hF00030008000;
ctrl0 <= '0;
ctrl1 <= '1;
ctrl2 <= '1;
end
else if (cyc == 2) begin
if (result_16_1 != 10) $stop;
if (result_16_2 != 16) $stop;
if (result_16_3 != 16) $stop;
if (result_32_1 != 24) $stop;
if (result_32_2 != 32) $stop;
if (result_32_3 != 32) $stop;
if (result_64_1 != 32) $stop;
if (result_64_2 != 64) $stop;
if (result_64_3 != 64) $stop;
if (result_10_3 != 10) $stop;
if (result_21_3 != 21) $stop;
if (result_59_3 != 59) $stop;
if (result_70_3 != 70) $stop;
in16 <= 16'h82B;
in32 <= 32'h305372;
in64 <= 64'h7777777777777777;
in10 <= 10'b1001_0111;
in21 <= 21'h91040C;
in59 <= 59'h12345678;
in70 <= 70'hF11111111;
// Confirm upper bits of the control arguments are ignored
ctrl0 <= 5;
ctrl1 <= 3;
ctrl2 <= 2;
end
else if (cyc == 3) begin
if (result_16_1 != 5) $stop;
if (result_16_2 != 5) $stop;
if (result_16_3 != 16) $stop;
if (result_32_1 != 10) $stop;
if (result_32_2 != 10) $stop;
if (result_32_3 != 32) $stop;
if (result_64_1 != 48) $stop;
if (result_64_2 != 48) $stop;
if (result_64_3 != 64) $stop;
if (result_10_3 != 10) $stop;
if (result_21_3 != 21) $stop;
if (result_59_3 != 59) $stop;
if (result_70_3 != 70) $stop;
end
else if (cyc == 4) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,4 @@
%Error: t/t_math_countbits_bad.v:14:54: Unsupported: $countbits with more than 3 control fields
14 | assign count = $countbits(32'h123456, '0, '1, 'x, 'z);
| ^~
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2019 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
compile(
fails => 1,
expect_filename => $Self->{golden_filename}
);
ok(1);
1;

View File

@ -0,0 +1,16 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 Yossi Nivin.
// SPDX-License-Identifier: CC0-1.0
module t(/*AUTOARG*/
// Inputs
clk
);
input clk;
integer count;
assign count = $countbits(32'h123456, '0, '1, 'x, 'z);
endmodule