parent
5d12ae3a2f
commit
84350859e0
|
|
@ -1143,15 +1143,15 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
if (isGlobalConstrained && !nodep->backp()) VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstCountOnes* nodep) override {
|
||||
// Convert it to (x & 1) + ((x & 2) >> 1) + ((x & 4) >> 2) + ...
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* const argp = nodep->lhsp()->unlinkFrBack();
|
||||
V3Number numOne{nodep, argp->width(), 1};
|
||||
// Build popcount expansion: (x & 1) + ((x & 2) >> 1) + ...
|
||||
// argp is consumed; caller must clone if reusing.
|
||||
AstNodeExpr* buildCountOnesExpansion(FileLine* fl, AstNodeExpr* argp,
|
||||
AstNodeExpr* dtypeNodep) {
|
||||
V3Number numOne{fl, argp->width(), 1};
|
||||
AstNodeExpr* sump = new AstAnd{fl, argp, new AstConst{fl, numOne}};
|
||||
sump->user1(true);
|
||||
for (int i = 1; i < argp->width(); i++) {
|
||||
V3Number numBitMask{nodep, argp->width(), 0};
|
||||
V3Number numBitMask{fl, argp->width(), 0};
|
||||
numBitMask.setBit(i, 1);
|
||||
AstAnd* const andp
|
||||
= new AstAnd{fl, argp->cloneTreePure(false), new AstConst{fl, numBitMask}};
|
||||
|
|
@ -1159,11 +1159,17 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
AstShiftR* const shiftp = new AstShiftR{
|
||||
fl, andp, new AstConst{fl, AstConst::WidthedValue{}, argp->width(), (uint32_t)i}};
|
||||
shiftp->user1(true);
|
||||
shiftp->dtypeFrom(nodep);
|
||||
sump = new AstAdd{nodep->fileline(), sump, shiftp};
|
||||
shiftp->dtypeFrom(dtypeNodep);
|
||||
sump = new AstAdd{fl, sump, shiftp};
|
||||
sump->user1(true);
|
||||
}
|
||||
// Restore the original width
|
||||
return sump;
|
||||
}
|
||||
|
||||
void visit(AstCountOnes* nodep) override {
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* const argp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* sump = buildCountOnesExpansion(fl, argp, nodep);
|
||||
if (nodep->width() > sump->width()) {
|
||||
sump = new AstExtend{fl, sump, nodep->width()};
|
||||
sump->user1(true);
|
||||
|
|
@ -1187,6 +1193,153 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
iterate(neqp);
|
||||
}
|
||||
void visit(AstOneHot* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
// $onehot(x) = (x != 0) && ((x & (x-1)) == 0)
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* const argp = nodep->lhsp()->unlinkFrBack();
|
||||
const int w = argp->width();
|
||||
|
||||
V3Number numZero{fl, w, 0};
|
||||
AstNeq* const neZerop
|
||||
= new AstNeq{fl, argp->cloneTreePure(false), new AstConst{fl, numZero}};
|
||||
neZerop->user1(true);
|
||||
|
||||
V3Number numOne{fl, w, 1};
|
||||
AstSub* const subp = new AstSub{fl, argp->cloneTreePure(false), new AstConst{fl, numOne}};
|
||||
subp->dtypeFrom(argp);
|
||||
subp->user1(true);
|
||||
|
||||
AstAnd* const andp = new AstAnd{fl, argp, subp};
|
||||
andp->dtypeFrom(argp);
|
||||
andp->user1(true);
|
||||
|
||||
V3Number numZero2{fl, w, 0};
|
||||
AstEq* const eqZerop = new AstEq{fl, andp, new AstConst{fl, numZero2}};
|
||||
eqZerop->user1(true);
|
||||
|
||||
AstLogAnd* const resultp = new AstLogAnd{fl, neZerop, eqZerop};
|
||||
resultp->user1(true);
|
||||
|
||||
nodep->replaceWith(resultp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
iterate(resultp);
|
||||
}
|
||||
void visit(AstOneHot0* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
// $onehot0(x) = (x & (x-1)) == 0
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* const argp = nodep->lhsp()->unlinkFrBack();
|
||||
const int w = argp->width();
|
||||
|
||||
V3Number numOne{fl, w, 1};
|
||||
AstSub* const subp = new AstSub{fl, argp->cloneTreePure(false), new AstConst{fl, numOne}};
|
||||
subp->dtypeFrom(argp);
|
||||
subp->user1(true);
|
||||
|
||||
AstAnd* const andp = new AstAnd{fl, argp, subp};
|
||||
andp->dtypeFrom(argp);
|
||||
andp->user1(true);
|
||||
|
||||
V3Number numZero{fl, w, 0};
|
||||
AstEq* const eqp = new AstEq{fl, andp, new AstConst{fl, numZero}};
|
||||
eqp->user1(true);
|
||||
|
||||
nodep->replaceWith(eqp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
iterate(eqp);
|
||||
}
|
||||
void visit(AstCountBits* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
FileLine* const fl = nodep->fileline();
|
||||
|
||||
bool countOnes = false;
|
||||
bool countZeros = false;
|
||||
for (AstNodeExpr* ctrlp : {nodep->rhsp(), nodep->thsp(), nodep->fhsp()}) {
|
||||
const AstConst* const cp = VN_CAST(ctrlp, Const);
|
||||
if (!cp) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: non-constant control in $countbits inside constraint");
|
||||
AstConst* const zerop
|
||||
= new AstConst{fl, AstConst::WidthedValue{}, nodep->width(), 0u};
|
||||
nodep->replaceWith(zerop);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
if (cp->num().bitIs1(0) && !countOnes)
|
||||
countOnes = true;
|
||||
else if (cp->num().bitIs0(0) && !countZeros)
|
||||
countZeros = true;
|
||||
}
|
||||
|
||||
AstNodeExpr* const argp = nodep->lhsp()->unlinkFrBack();
|
||||
const int argWidth = argp->width();
|
||||
AstNodeExpr* sump = nullptr;
|
||||
if (countOnes && countZeros) {
|
||||
// ones + zeros = width for 2-state types
|
||||
sump = new AstConst{fl, AstConst::WidthedValue{}, nodep->width(), (uint32_t)argWidth};
|
||||
VL_DO_DANGLING(argp->deleteTree(), argp);
|
||||
} else if (countOnes) {
|
||||
sump = buildCountOnesExpansion(fl, argp, nodep);
|
||||
} else if (countZeros) {
|
||||
// width - countones(x)
|
||||
AstNodeExpr* const onesCountp = buildCountOnesExpansion(fl, argp, nodep);
|
||||
V3Number widthVal{nodep, onesCountp->width(), (uint32_t)argWidth};
|
||||
sump = new AstSub{fl, new AstConst{fl, widthVal}, onesCountp};
|
||||
sump->dtypeFrom(onesCountp);
|
||||
sump->user1(true);
|
||||
} else {
|
||||
sump = new AstConst{fl, AstConst::WidthedValue{}, nodep->width(), 0u};
|
||||
VL_DO_DANGLING(argp->deleteTree(), argp);
|
||||
}
|
||||
|
||||
if (nodep->width() > sump->width()) {
|
||||
sump = new AstExtend{fl, sump, nodep->width()};
|
||||
sump->user1(true);
|
||||
} else if (nodep->width() < sump->width()) {
|
||||
sump = new AstSel{fl, sump, 0, nodep->width()};
|
||||
sump->user1(true);
|
||||
}
|
||||
|
||||
nodep->replaceWith(sump);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
iterate(sump);
|
||||
}
|
||||
void visit(AstCLog2* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
// $clog2(x): ITE chain (x<=1)?0 : (x<=2)?1 : ... : argWidth
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstNodeExpr* const argp = nodep->lhsp()->unlinkFrBack();
|
||||
const int argWidth = argp->width();
|
||||
const int resultWidth = nodep->width();
|
||||
|
||||
AstNodeExpr* resultp
|
||||
= new AstConst{fl, AstConst::WidthedValue{}, resultWidth, (uint32_t)argWidth};
|
||||
|
||||
for (int k = argWidth - 1; k >= 0; k--) {
|
||||
V3Number threshold{fl, argWidth, 0};
|
||||
if (k < 32)
|
||||
threshold.setLong(1ULL << k);
|
||||
else
|
||||
threshold.setBit(k, 1);
|
||||
|
||||
AstLte* const ltep
|
||||
= new AstLte{fl, argp->cloneTreePure(false), new AstConst{fl, threshold}};
|
||||
ltep->user1(true);
|
||||
|
||||
AstConst* const valuep
|
||||
= new AstConst{fl, AstConst::WidthedValue{}, resultWidth, (uint32_t)k};
|
||||
|
||||
resultp = new AstCond{fl, ltep, valuep, resultp};
|
||||
resultp->dtypeChgWidthSigned(resultWidth, resultWidth, VSigning::SIGNED);
|
||||
resultp->user1(true);
|
||||
}
|
||||
|
||||
VL_DO_DANGLING(argp->deleteTree(), argp);
|
||||
nodep->replaceWith(resultp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
iterate(resultp);
|
||||
}
|
||||
void visit(AstNodeBiop* nodep) override {
|
||||
if (editFormat(nodep)) return;
|
||||
editSMT(nodep, nodep->lhsp(), nodep->rhsp());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
%Error-UNSUPPORTED: t/t_constraint_countbits_unsup.v:12:21: Unsupported: non-constant control in $countbits inside constraint
|
||||
12 | constraint cons { $countbits(value, ctrl) == 3; }
|
||||
| ^~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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-FileCopyrightText: 2024 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by PlanV GmbH.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test: non-constant control in $countbits inside constraint (unsupported)
|
||||
|
||||
class Packet;
|
||||
rand bit [7:0] value;
|
||||
bit ctrl;
|
||||
constraint cons { $countbits(value, ctrl) == 3; }
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Packet p;
|
||||
|
||||
initial begin
|
||||
p = new;
|
||||
void'(p.randomize());
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2026 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
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
if not test.have_solver:
|
||||
test.skip("No constraint solver installed")
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by PlanV GmbH.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// Test: System functions ($onehot, $onehot0, $countbits, $clog2) inside
|
||||
// constraint blocks (IEEE 1800-2017 Section 18.5.12)
|
||||
|
||||
class test_onehot;
|
||||
rand bit [7:0] value;
|
||||
constraint c_hot { $onehot(value); }
|
||||
endclass
|
||||
|
||||
class test_onehot0;
|
||||
rand bit [7:0] value;
|
||||
constraint c_hot0 { $onehot0(value); }
|
||||
endclass
|
||||
|
||||
class test_countbits_ones;
|
||||
rand bit [7:0] value;
|
||||
constraint c_bits { $countbits(value, '1) == 3; }
|
||||
endclass
|
||||
|
||||
class test_countbits_zeros;
|
||||
rand bit [7:0] value;
|
||||
constraint c_bits { $countbits(value, '0) == 6; } // 6 zeros = 2 ones
|
||||
endclass
|
||||
|
||||
class test_clog2;
|
||||
rand bit [7:0] data_width;
|
||||
rand bit [7:0] addr_bits;
|
||||
constraint c_log { addr_bits == 8'($clog2(data_width)); }
|
||||
constraint c_nonzero { data_width > 0; }
|
||||
endclass
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
automatic test_onehot oh = new;
|
||||
automatic test_onehot0 oh0 = new;
|
||||
automatic test_countbits_ones cb1 = new;
|
||||
automatic test_countbits_zeros cb0 = new;
|
||||
automatic test_clog2 cl = new;
|
||||
automatic bit ok = 1'b1;
|
||||
|
||||
// Test $onehot: exactly one bit set
|
||||
repeat(20) begin
|
||||
if (oh.randomize() == 0) $fatal(1, "$onehot randomize failed");
|
||||
if ($onehot(oh.value) !== 1'b1) begin
|
||||
$display("FAIL: $onehot value=%08b, $onehot=%0b", oh.value, $onehot(oh.value));
|
||||
ok = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// Test $onehot0: zero or one bit set
|
||||
repeat(20) begin
|
||||
if (oh0.randomize() == 0) $fatal(1, "$onehot0 randomize failed");
|
||||
if ($onehot0(oh0.value) !== 1'b1) begin
|
||||
$display("FAIL: $onehot0 value=%08b, $onehot0=%0b", oh0.value, $onehot0(oh0.value));
|
||||
ok = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// Test $countbits counting ones: exactly 3 ones
|
||||
repeat(20) begin
|
||||
if (cb1.randomize() == 0) $fatal(1, "$countbits('1) randomize failed");
|
||||
if ($countbits(cb1.value, '1) != 3) begin
|
||||
$display("FAIL: $countbits('1) value=%08b, count=%0d",
|
||||
cb1.value, $countbits(cb1.value, '1));
|
||||
ok = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// Test $countbits counting zeros: exactly 6 zeros (= 2 ones)
|
||||
repeat(20) begin
|
||||
if (cb0.randomize() == 0) $fatal(1, "$countbits('0) randomize failed");
|
||||
if ($countbits(cb0.value, '0) != 6) begin
|
||||
$display("FAIL: $countbits('0) value=%08b, zeros=%0d ones=%0d",
|
||||
cb0.value, $countbits(cb0.value, '0), $countbits(cb0.value, '1));
|
||||
ok = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// Test $clog2: addr_bits == $clog2(data_width)
|
||||
repeat(20) begin
|
||||
if (cl.randomize() == 0) $fatal(1, "$clog2 randomize failed");
|
||||
if (cl.addr_bits != 8'($clog2(cl.data_width))) begin
|
||||
$display("FAIL: $clog2 data_width=%0d, addr_bits=%0d, expected=%0d",
|
||||
cl.data_width, cl.addr_bits, $clog2(cl.data_width));
|
||||
ok = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
if (ok) $display("All tests passed");
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
%Error-UNSUPPORTED: t/t_constraint_unsup.v:9:22: Unsupported expression inside constraint
|
||||
9 | constraint cons { $onehot(m_one) == 1; }
|
||||
| ^~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_constraint_unsup.v:9:27: Unsupported expression inside constraint
|
||||
9 | constraint cons { m_one ** 2 > 0; }
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2025 Antmicro
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2026 by PlanV GmbH.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Packet;
|
||||
rand int m_one;
|
||||
constraint cons { $onehot(m_one) == 1; }
|
||||
rand int m_one;
|
||||
constraint cons { m_one ** 2 > 0; }
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Packet p;
|
||||
Packet p;
|
||||
|
||||
initial begin
|
||||
p = new;
|
||||
void'(p.randomize());
|
||||
end
|
||||
initial begin
|
||||
p = new;
|
||||
void'(p.randomize());
|
||||
end
|
||||
endmodule
|
||||
|
|
|
|||
Loading…
Reference in New Issue