From f9a0cf0cff29150fd6ef76ed9c4ddec650f63473 Mon Sep 17 00:00:00 2001 From: Yossi Nivin Date: Sun, 10 May 2020 20:27:22 +0200 Subject: [PATCH] Support $countbits (#2287) --- Changes | 2 + bin/verilator | 16 +-- docs/CONTRIBUTORS | 1 + include/verilated.h | 30 ++++++ src/V3Ast.h | 37 +++++++ src/V3AstNodes.h | 27 +++++ src/V3Cast.cpp | 9 ++ src/V3Clean.cpp | 13 +++ src/V3Const.cpp | 9 ++ src/V3EmitC.cpp | 20 ++++ src/V3EmitV.cpp | 9 +- src/V3Number.cpp | 49 +++++++++ src/V3Number.h | 4 + src/V3Simulate.h | 11 ++ src/V3Width.cpp | 12 +++ src/verilog.l | 1 + src/verilog.y | 6 ++ test_regress/t/t_math_countbits.pl | 21 ++++ test_regress/t/t_math_countbits.v | 134 ++++++++++++++++++++++++ test_regress/t/t_math_countbits_bad.out | 4 + test_regress/t/t_math_countbits_bad.pl | 19 ++++ test_regress/t/t_math_countbits_bad.v | 16 +++ 22 files changed, 441 insertions(+), 9 deletions(-) create mode 100755 test_regress/t/t_math_countbits.pl create mode 100644 test_regress/t/t_math_countbits.v create mode 100755 test_regress/t/t_math_countbits_bad.out create mode 100755 test_regress/t/t_math_countbits_bad.pl create mode 100644 test_regress/t/t_math_countbits_bad.v diff --git a/Changes b/Changes index 24ac0fe9f..e85264813 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,8 @@ The contributors that suggested a given feature are shown in []. Thanks! * Verilator 4.035 devel +**** Support $countbits. (#2287) [Yossi Nivin] + **** Support $isunbounded and parameter $. (#2104) **** Support unpacked array .sum and .product. diff --git a/bin/verilator b/bin/verilator index c0324a0c3..f01769c2b 100755 --- a/bin/verilator +++ b/bin/verilator @@ -3104,11 +3104,11 @@ uwire keyword. =head2 SystemVerilog 2005 (IEEE 1800-2005) Support Verilator supports ==? and !=? operators, ++ and -- in some contexts, -$bits, $countones, $error, $fatal, $info, $isunknown, $onehot, $onehot0, -$unit, $warning, always_comb, always_ff, always_latch, bit, byte, chandle, -const, do-while, enum, export, final, import, int, interface, logic, -longint, modport, package, program, shortint, struct, time, typedef, union, -var, void, priority case/if, and unique case/if. +$bits, $countbits, $countones, $error, $fatal, $info, $isunknown, $onehot, +$onehot0, $unit, $warning, always_comb, always_ff, always_latch, bit, byte, +chandle, const, do-while, enum, export, final, import, int, interface, +logic, longint, modport, package, program, shortint, struct, time, typedef, +union, var, void, priority case/if, and unique case/if. 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 keyword as if it were the normal wire keyword. -=item $bits, $countones, $error, $fatal, $finish, $info, $isunknown, -$onehot, $onehot0, $readmemb, $readmemh, $signed, $stime, $stop, $time, -$unsigned, $warning. +=item $bits, $countbits, $countones, $error, $fatal, $finish, $info, +$isunknown, $onehot, $onehot0, $readmemb, $readmemh, $signed, $stime, $stop, +$time, $unsigned, $warning. Generally supported. diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index e4c77fb32..aebca57e0 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -47,5 +47,6 @@ Tobias Wölfel Todd Strader Veripool API Bot Wilson Snyder +Yossi Nivin Yutetsu TAKATSUKASA Yves Mathieu diff --git a/include/verilated.h b/include/verilated.h index 06a70c978..e06a2e275 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -1217,6 +1217,36 @@ static inline IData VL_COUNTONES_W(int words, WDataInP lwp) VL_MT_SAFE { 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(lhs), ctrl0, ctrl1, ctrl2) + + VL_COUNTBITS_I(lbits - 32, static_cast(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 { return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); } diff --git a/src/V3Ast.h b/src/V3Ast.h index 13cc8be24..c99d7c235 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1999,6 +1999,43 @@ public: 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 { // Binary math with commutative properties public: diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 39e91aec7..95ece193f 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -5424,6 +5424,33 @@ public: virtual bool sizeMattersLhs() const { return false; } 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 { // Number of bits set in vector public: diff --git a/src/V3Cast.cpp b/src/V3Cast.cpp index 9c716e75b..7b5c70079 100644 --- a/src/V3Cast.cpp +++ b/src/V3Cast.cpp @@ -128,6 +128,15 @@ private: if (nodep->sizeMattersRhs()) ensureCast(nodep->rhsp()); 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 { iterateChildren(nodep); ensureLower32Cast(nodep); diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 164463364..6bf301174 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -160,6 +160,15 @@ private: if (nodep->cleanThs()) ensureClean(nodep->thsp()); // 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 virtual void visit(AstNodeModule* nodep) VL_OVERRIDE { @@ -192,6 +201,10 @@ private: operandBiop(nodep); 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 { iterateChildren(nodep); computeCppWidth(nodep); diff --git a/src/V3Const.cpp b/src/V3Const.cpp index c935aaf81..4f8419545 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -664,6 +664,14 @@ private: UINFO(4, "TRICONST -> " << num << endl); 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) { // 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 TREEOPC("AstNodeBiop {$lhsp.castConst, $rhsp.castConst}", "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 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 diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 016bb6b3f..011fe47d0 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -1000,6 +1000,26 @@ public: emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)", nodep->lhsp(), 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 virtual void visit(AstVarRef* nodep) VL_OVERRIDE { puts(nodep->hiernameProtect()); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 3fee6c161..bea707289 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -411,13 +411,15 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { // Operators 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, // and write out appropriate text. // %f Potential fileline-if-change and line break // %l lhsp - if appropriate // %r rhsp - if appropriate // %t thsp - if appropriate + // %o fhsp - if appropriate // %d dtypep - if appropriate // %k Potential line break bool inPct = false; @@ -450,6 +452,11 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { iterateAndNextNull(thsp); break; } + case 'o': { + UASSERT_OBJ(thsp, nodep, "emitVerilog() references undef node"); + iterateAndNextNull(fhsp); + break; + } case 'd': { UASSERT_OBJ(nodep->dtypep(), nodep, "emitVerilog() references undef node"); iterateAndNextNull(nodep->dtypep()); diff --git a/src/V3Number.cpp b/src/V3Number.cpp index d23800e08..85b61e913 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -38,6 +38,9 @@ #define NUM_ASSERT_OP_ARGS3(arg1, arg2, arg3) \ UASSERT((this != &(arg1) && this != &(arg2) && this != &(arg3)), \ "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) \ UASSERT((!(arg1).isDouble() && !(arg1).isString()), \ @@ -47,6 +50,12 @@ NUM_ASSERT_LOGIC_ARGS1(arg1); \ 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) \ UASSERT((arg1).isString(), \ "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 } +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 { int n = 0; for (int bit = 0; bit < this->width(); bit++) { @@ -1095,6 +1135,15 @@ V3Number& V3Number::opRedXnor(const V3Number& lhs) { 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) { NUM_ASSERT_OP_ARGS1(lhs); NUM_ASSERT_LOGIC_ARGS1(lhs); diff --git a/src/V3Number.h b/src/V3Number.h index 016796dcb..fb2fc6cd5 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -284,6 +284,8 @@ public: uint32_t toHash() const; uint32_t edataWord(int eword) 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 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& opRedXor(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& opIsUnknown(const V3Number& lhs); V3Number& opOneHot(const V3Number& lhs); diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 2d9e018e9..c229d09f1 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -539,6 +539,17 @@ private: 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 { // Need to short circuit if (!optimizable()) return; // Accelerate diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a47743257..4b94616be 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1140,6 +1140,18 @@ private: userIterateAndNext(nodep->lhsp(), 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 { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->lhsp(), SELF, BOTH); diff --git a/src/verilog.l b/src/verilog.l index 56aa5c59f..4b9366a1f 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -434,6 +434,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} { /* System Tasks */ "$bits" { FL; return yD_BITS; } + "$countbits" { FL; return yD_COUNTBITS; } "$countones" { FL; return yD_COUNTONES; } "$dimensions" { FL; return yD_DIMENSIONS; } "$error" { FL; return yD_ERROR; } diff --git a/src/verilog.y b/src/verilog.y index fd52d94b5..d20964039 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -552,6 +552,7 @@ class AstSenTree; %token yD_CLOG2 "$clog2" %token yD_COS "$cos" %token yD_COSH "$cosh" +%token yD_COUNTBITS "$countbits" %token yD_COUNTONES "$countones" %token yD_DIMENSIONS "$dimensions" %token yD_DISPLAY "$display" @@ -3391,6 +3392,11 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task or func) | yD_CLOG2 '(' expr ')' { $$ = new AstCLog2($1,$3); } | yD_COS '(' expr ')' { $$ = new AstCosD($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_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_DIMENSIONS,$3); } | yD_EXP '(' expr ')' { $$ = new AstExpD($1,$3); } diff --git a/test_regress/t/t_math_countbits.pl b/test_regress/t/t_math_countbits.pl new file mode 100755 index 000000000..9a15dd2cc --- /dev/null +++ b/test_regress/t/t_math_countbits.pl @@ -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; diff --git a/test_regress/t/t_math_countbits.v b/test_regress/t/t_math_countbits.v new file mode 100644 index 000000000..617d2b47d --- /dev/null +++ b/test_regress/t/t_math_countbits.v @@ -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 diff --git a/test_regress/t/t_math_countbits_bad.out b/test_regress/t/t_math_countbits_bad.out new file mode 100755 index 000000000..9e676ad75 --- /dev/null +++ b/test_regress/t/t_math_countbits_bad.out @@ -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 diff --git a/test_regress/t/t_math_countbits_bad.pl b/test_regress/t/t_math_countbits_bad.pl new file mode 100755 index 000000000..6b252273b --- /dev/null +++ b/test_regress/t/t_math_countbits_bad.pl @@ -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; diff --git a/test_regress/t/t_math_countbits_bad.v b/test_regress/t/t_math_countbits_bad.v new file mode 100644 index 000000000..6560d2303 --- /dev/null +++ b/test_regress/t/t_math_countbits_bad.v @@ -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