Skip to content

Commit

Permalink
Support basic dist constraints (verilator#5431)
Browse files Browse the repository at this point in the history
  • Loading branch information
kozdra authored Sep 12, 2024
1 parent 140eb0a commit 0b7510b
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 42 deletions.
7 changes: 1 addition & 6 deletions src/V3Randomize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -640,11 +640,6 @@ class ConstraintExprVisitor final : public VNVisitor {
// Fall back to "(ite cond then else)"
visit(static_cast<AstNodeTriop*>(nodep));
}
void visit(AstDist* nodep) override {
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (unsupported)");
nodep->replaceWith(new AstSFormatF{nodep->fileline(), "true", false, nullptr});
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void visit(AstReplicate* nodep) override {
// Biop, but RHS is harmful
if (editFormat(nodep)) return;
Expand Down Expand Up @@ -720,7 +715,7 @@ class ConstraintExprVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
void visit(AstConstraintBefore* nodep) override {
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (unsupported)");
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
void visit(AstConstraintUnique* nodep) override {
Expand Down
86 changes: 52 additions & 34 deletions src/V3Width.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2614,6 +2614,8 @@ class WidthVisitor final : public VNVisitor {
}
}
void visit(AstDist* nodep) override {
// x dist {a :/ p, b :/ q} --> (p > 0 && x == a) || (q > 0 && x == b)
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)");
userIterateAndNext(nodep->exprp(), WidthVP{CONTEXT_DET, PRELIM}.p());
for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
nextip = itemp->nextp(); // iterate may cause the node to get replaced
Expand Down Expand Up @@ -2643,14 +2645,31 @@ class WidthVisitor final : public VNVisitor {

iterateCheck(nodep, "Dist expression", nodep->exprp(), CONTEXT_DET, FINAL, subDTypep,
EXTEND_EXP);
for (AstDistItem *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
nextip
= VN_AS(itemp->nextp(), DistItem); // iterate may cause the node to get replaced
iterateCheck(nodep, "Dist Item", itemp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
nextip = itemp->nextp();
itemp = VN_AS(itemp, DistItem)->rangep();
// InsideRange will get replaced with Lte&Gte and finalized later
if (!VN_IS(itemp, InsideRange))
iterateCheck(nodep, "Dist Item", itemp, CONTEXT_DET, FINAL, subDTypep, EXTEND_EXP);
}
AstNodeExpr* newp = nullptr;
for (AstDistItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), DistItem)) {
AstNodeExpr* inewp = insideItem(nodep, nodep->exprp(), itemp->rangep());
if (!inewp) continue;
AstNodeExpr* const cmpp
= new AstGt{itemp->fileline(), itemp->weightp()->unlinkFrBack(),
new AstConst{itemp->fileline(), 0}};
cmpp->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true);
cmpp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true);
inewp = new AstLogAnd{itemp->fileline(), cmpp, inewp};
newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp;
}
if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};

if (debug() >= 9) nodep->dumpTree("- dist-out: ");
nodep->dtypep(subDTypep);
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}

void visit(AstInside* nodep) override {
Expand Down Expand Up @@ -2696,41 +2715,40 @@ class WidthVisitor final : public VNVisitor {
AstNodeExpr* newp = nullptr;
for (AstNodeExpr *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
nextip = VN_AS(itemp->nextp(), NodeExpr); // Will be unlinking
AstNodeExpr* inewp;
const AstNodeDType* const itemDtp = itemp->dtypep()->skipRefp();
if (AstInsideRange* const irangep = VN_CAST(itemp, InsideRange)) {
// Similar logic in V3Case
inewp = irangep->newAndFromInside(nodep->exprp(), irangep->lhsp()->unlinkFrBack(),
irangep->rhsp()->unlinkFrBack());
} else if (VN_IS(itemDtp, UnpackArrayDType) || VN_IS(itemDtp, DynArrayDType)
|| VN_IS(itemDtp, QueueDType)) {
// Unsupported in parameters
AstNodeExpr* exprp = nodep->exprp()->cloneTreePure(true);
inewp = new AstCMethodHard{nodep->fileline(), itemp->unlinkFrBack(), "inside",
exprp};
iterateCheckTyped(nodep, "inside value", exprp, itemDtp->subDTypep(), BOTH);
VL_DANGLING(exprp); // Might have been replaced
inewp->dtypeSetBit();
inewp->didWidth(true);
} else if (VN_IS(itemDtp, AssocArrayDType)) {
nodep->v3error("Inside operator not specified on associative arrays "
"(IEEE 1800-2023 11.4.13)");
continue;
} else {
inewp = AstEqWild::newTyped(itemp->fileline(), nodep->exprp()->cloneTreePure(true),
itemp->unlinkFrBack());
}
if (newp) {
newp = new AstLogOr{nodep->fileline(), newp, inewp};
} else {
newp = inewp;
}
AstNodeExpr* const inewp = insideItem(nodep, nodep->exprp(), itemp);
if (!inewp) continue;
newp = newp ? new AstLogOr{nodep->fileline(), newp, inewp} : inewp;
}
if (!newp) newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
if (debug() >= 9) newp->dumpTree("- inside-out: ");
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
AstNodeExpr* insideItem(AstNode* nodep, AstNodeExpr* exprp, AstNodeExpr* itemp) {
const AstNodeDType* const itemDtp = itemp->dtypep()->skipRefp();
if (AstInsideRange* const irangep = VN_CAST(itemp, InsideRange)) {
// Similar logic in V3Case
return irangep->newAndFromInside(exprp, irangep->lhsp()->unlinkFrBack(),
irangep->rhsp()->unlinkFrBack());
} else if (VN_IS(itemDtp, UnpackArrayDType) || VN_IS(itemDtp, DynArrayDType)
|| VN_IS(itemDtp, QueueDType)) {
// Unsupported in parameters
AstNodeExpr* const cexprp = exprp->cloneTreePure(true);
AstNodeExpr* const inewp
= new AstCMethodHard{nodep->fileline(), itemp->unlinkFrBack(), "inside", cexprp};
iterateCheckTyped(nodep, "inside value", cexprp, itemDtp->subDTypep(), BOTH);
VL_DANGLING(cexprp); // Might have been replaced
inewp->dtypeSetBit();
inewp->didWidth(true);
return inewp;
} else if (VN_IS(itemDtp, AssocArrayDType)) {
nodep->v3error("Inside operator not specified on associative arrays "
"(IEEE 1800-2023 11.4.13)");
return nullptr;
}
return AstEqWild::newTyped(itemp->fileline(), exprp->cloneTreePure(true),
itemp->unlinkFrBack());
}
void visit(AstInsideRange* nodep) override {
// Just do each side; AstInside will rip these nodes out later
userIterateAndNext(nodep->lhsp(), m_vup);
Expand Down
21 changes: 21 additions & 0 deletions test_regress/t/t_constraint_dist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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(verilator_flags2=['-Wno-CONSTRAINTIGN'])

test.execute()

test.passes()
39 changes: 39 additions & 0 deletions test_regress/t/t_constraint_dist.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0

`define check_rand(cl, field, cond) \
begin \
longint prev_result; \
int ok = 0; \
for (int i = 0; i < 10; i++) begin \
longint result; \
if (!bit'(cl.randomize())) $stop; \
result = longint'(field); \
if (!(cond)) $stop; \
if (i > 0 && result != prev_result) ok = 1; \
prev_result = result; \
end \
if (ok != 1) $stop; \
end

class C;
rand int x, y;
constraint distrib {
x dist { [1:3] := 0, [5:6], [9:15] :/ 0 };
y dist { [1:3] := 0, 5, 6 := 8, [9:15] :/ 0 };
x < 20;
};
endclass

module t;
initial begin
C c = new;
`check_rand(c, c.x, 5 <= c.x && c.x <= 6);
`check_rand(c, c.y, 5 <= c.y && c.y <= 6);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
17 changes: 17 additions & 0 deletions test_regress/t/t_constraint_dist_unsup.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
%Warning-CONSTRAINTIGN: t/t_constraint_dist_unsup.v:27:10: Constraint expression ignored (imperfect distribution)
: ... note: In instance 't'
27 | x dist {que};
| ^~~~
... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest
... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message.
%Warning-CONSTRAINTIGN: t/t_constraint_dist_unsup.v:28:10: Constraint expression ignored (imperfect distribution)
: ... note: In instance 't'
28 | y dist {arr};
| ^~~~
%Warning-CONSTRAINTIGN: t/t_constraint_dist_unsup.v:27:10: Unsupported: randomizing this expression, treating as state
27 | x dist {que};
| ^~~~
%Warning-CONSTRAINTIGN: t/t_constraint_dist_unsup.v:28:10: Unsupported: randomizing this expression, treating as state
28 | y dist {arr};
| ^~~~
%Error: Exiting due to
16 changes: 16 additions & 0 deletions test_regress/t/t_constraint_dist_unsup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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('linter')

test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)

test.passes()
40 changes: 40 additions & 0 deletions test_regress/t/t_constraint_dist_unsup.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0

`define check_rand(cl, field, cond) \
begin \
longint prev_result; \
int ok = 0; \
for (int i = 0; i < 10; i++) begin \
longint result; \
if (!bit'(cl.randomize())) $stop; \
result = longint'(field); \
if (!(cond)) $stop; \
if (i > 0 && result != prev_result) ok = 1; \
prev_result = result; \
end \
if (ok != 1) $stop; \
end

class C;
int que[$] = '{3, 4, 5};
int arr[3] = '{5, 6, 7};
rand int x, y;
constraint distrib {
x dist {que};
y dist {arr};
};
endclass

module t;
initial begin
C c = new;
`check_rand(c, c.x, 3 <= c.x && c.x <= 5);
`check_rand(c, c.y, 5 <= c.y && c.y <= 7);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
4 changes: 2 additions & 2 deletions test_regress/t/t_randomize.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%Warning-CONSTRAINTIGN: t/t_randomize.v:22:14: Constraint expression ignored (unsupported)
%Warning-CONSTRAINTIGN: t/t_randomize.v:22:14: Constraint expression ignored (imperfect distribution)
: ... note: In instance 't'
22 | length dist { [0:1], [2:5] :/ 2, 6 := 6, 7 := 10, 1};
| ^~~~
Expand All @@ -8,7 +8,7 @@
: ... note: In instance 't'
40 | unique { array[0], array[1] };
| ^~~~~~
%Warning-CONSTRAINTIGN: t/t_randomize.v:43:23: Constraint expression ignored (unsupported)
%Warning-CONSTRAINTIGN: t/t_randomize.v:43:23: Constraint expression ignored (imperfect distribution)
: ... note: In instance 't'
43 | constraint order { solve length before header; }
| ^~~~~
Expand Down

0 comments on commit 0b7510b

Please sign in to comment.