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
**** Support $countbits. (#2287) [Yossi Nivin]
**** Support $isunbounded and parameter $. (#2104)
**** Support unpacked array .sum and .product.

View File

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

View File

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

View File

@ -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<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 {
return (((lhs & (lhs - 1)) == 0) & (lhs != 0));
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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());

View File

@ -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());

View File

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

View File

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

View File

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

View File

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

View File

@ -434,6 +434,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
<S05,S09,S12,S17,SAX>{
/* 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; }

View File

@ -552,6 +552,7 @@ class AstSenTree;
%token<fl> yD_CLOG2 "$clog2"
%token<fl> yD_COS "$cos"
%token<fl> yD_COSH "$cosh"
%token<fl> yD_COUNTBITS "$countbits"
%token<fl> yD_COUNTONES "$countones"
%token<fl> yD_DIMENSIONS "$dimensions"
%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_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); }

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