Skip to content

Commit

Permalink
Merge pull request #28 from antmicro/randomize-constraints-crave-with
Browse files Browse the repository at this point in the history
Support obj.randomize() with {...}
  • Loading branch information
kozdra authored Dec 9, 2022
2 parents 77d064b + 931a782 commit c975a61
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 63 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ jobs:
uses: actions/checkout@v2
with:
path: repo
submodules: recursive

- name: Cache $CCACHE_DIR
uses: actions/cache@v2
Expand Down
2 changes: 1 addition & 1 deletion crave
9 changes: 9 additions & 0 deletions include/verilated_random.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,13 @@
#define VERILATOR_VERILATED_RANDOM_H_

#include <crave/ConstrainedRandom.hpp>

template <typename Func>
inline IData VL_RANDOMIZE_WITH(const crave::Generator& constraint, Func with_func) {
crave::Generator gen;
gen.merge(constraint);
gen.hard(with_func(QData{}, gen));
return gen.next();
}

#endif // Guard
1 change: 1 addition & 0 deletions src/V3Clean.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class CleanVisitor final : public VNVisitor {
if (!nodep->user2() && nodep->hasDType()) {
if (VN_IS(nodep, Var)
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|| VN_IS(nodep, LambdaArgRef) // Or arguments!
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
Expand Down
1 change: 1 addition & 0 deletions src/V3Descope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class DescopeVisitor final : public VNVisitor {
// module.
string descopedSelfPointer(const AstScope* scopep) {
UASSERT(scopep, "Var/Func not scoped");
UASSERT_OBJ(m_funcp, scopep, "Var/Func outside of CFunc");
// Static functions can't use relative references via 'this->'
const bool relativeRefOk = !m_funcp->isStatic();

Expand Down
3 changes: 2 additions & 1 deletion src/V3EmitCFunc.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,8 @@ class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
puts(",");
}
if (auto* const argrefp = nodep->valueArgRefp()) {
putbs(argrefp->dtypep()->cType(argrefp->nameProtect(), false, false));
putbs(argrefp->dtypep()->cType(argrefp->nameProtect(), false,
argrefp->dtypep()->basicp()->isRandomGenerator()));
}
// Probably fragile, V3Task may need to convert to a AstCReturn
puts(") { return ");
Expand Down
37 changes: 15 additions & 22 deletions src/V3LinkDot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1438,28 +1438,6 @@ class LinkDotFindVisitor final : public VNVisitor {
iterateAndNextNull(nodep->exprp());
}
}
AstNode* m_fromp = nullptr;
virtual void visit(AstDot* nodep) override {
if (nodep->user1()) return;
if (auto* withParsep = VN_CAST(nodep->rhsp(), WithParse)) {
if (withParsep->funcrefp()->name() == "randomize") {
m_fromp = nodep->lhsp();
iterateAndNextNull(withParsep->exprp());
m_fromp = nullptr;
}
}
iterateChildren(nodep);
}
virtual void visit(AstParseRef* nodep) override {
if (m_fromp) {
auto* dotp = new AstDot(nodep->fileline(), false, m_fromp->cloneTree(false),
nodep->cloneTree(false));
nodep->replaceWith(dotp);
dotp->user1SetOnce();
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}

void visit(AstNode* nodep) override { iterateChildren(nodep); }

public:
Expand Down Expand Up @@ -1952,6 +1930,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
VSymEnt* m_modSymp = nullptr; // SymEnt for current module
VSymEnt* m_pinSymp = nullptr; // SymEnt for pin lookups
const AstCell* m_cellp = nullptr; // Current cell
AstNode* m_fromp = nullptr; // Current randomized variable
AstNodeModule* m_modp = nullptr; // Current module
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
int m_modportNum = 0; // Uniqueify modport numbers
Expand Down Expand Up @@ -2389,6 +2368,18 @@ class LinkDotResolveVisitor final : public VNVisitor {
UINFO(1, "ds=" << m_ds.ascii() << endl);
nodep->v3fatalSrc("Unhandled VParseRefExp");
}
if (allowVar && m_fromp
&& (m_ds.m_dotPos == DP_NONE || m_ds.m_dotp->lhsp() == nodep)) {
auto* const classRefp
= VN_AS(VN_AS(m_fromp, VarRef)->varp()->subDTypep(), ClassRefDType);
if (classRefp->classp()->findMember(nodep->name())) {
nodep->replaceWith(new AstMemberSel{nodep->fileline(),
m_fromp->cloneTree(false),
VFlagChildDType{}, nodep->name()});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
}
// Lookup
VSymEnt* foundp;
string baddot;
Expand Down Expand Up @@ -2753,8 +2744,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
void visit(AstMethodCall* nodep) override {
// Created here so should already be resolved.
VL_RESTORER(m_ds);
VL_RESTORER(m_fromp);
{
m_ds.init(m_curSymp);
if (nodep->name() == "randomize") m_fromp = nodep->fromp();
iterateChildren(nodep);
}
}
Expand Down
115 changes: 78 additions & 37 deletions src/V3Randomize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,17 @@ class RandomizeVisitor final : public VNVisitor {
// NODE STATE
// Cleared on Netlist
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
// AstMemberSel::user1() -> bool. Set to true if already processed
// AstEnumDType::user2() -> AstVar*. Pointer to table with enum values
// VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor)
const VNUser2InUse m_inuser2;

// STATE
size_t m_enumValueTabCount = 0; // Number of tables with enum values created
AstNodeModule* m_modp;
AstNode* m_fromp; // Current randomized variable
bool m_inMemberSel; // True if iterating below a MemberSel
bool m_hasRandVarRef; // True if a reference to the randomized variable was found

// METHODS
AstVar* enumValueTabp(AstEnumDType* nodep) {
Expand Down Expand Up @@ -268,8 +272,6 @@ class RandomizeVisitor final : public VNVisitor {
= createRef(nodep->fileline(), memberVarp, fromp, VAccess::WRITE);
stmtsp = AstNode::addNext(stmtsp, newRandStmtsp(nodep->fileline(), refp));
} else if (const auto* const classRefp = VN_CAST(dtypep, ClassRefDType)) {
auto* const refp
= new AstVarRef(nodep->fileline(), memberVarp, VAccess::WRITE);
stmtsp = AstNode::addNext(
stmtsp, newClassRandStmtsp(classRefp->classp(),
createRef(nodep->fileline(), memberVarp, fromp,
Expand Down Expand Up @@ -320,7 +322,7 @@ class RandomizeVisitor final : public VNVisitor {
m_modp = nodep;
if (!nodep->user1()) return; // Doesn't need randomize, or already processed
UINFO(9, "Define randomize() for " << nodep << endl);
auto* relaxp = newRelaxNextSoft(nodep);
newRelaxNextSoft(nodep);
auto* funcp = V3Randomize::newRandomizeFunc(nodep);
auto* genp = new AstVar(fl, VVarType::MEMBER, "constraint",
nodep->findBasicDType(VBasicDTypeKwd::RANDOM_GENERATOR));
Expand All @@ -338,6 +340,8 @@ class RandomizeVisitor final : public VNVisitor {
auto* taskp = V3Randomize::newSetupConstraintsTask(nodep);
newp->addStmtsp(new AstTaskRef{fl, taskp->name(), nullptr});

iterateChildren(nodep);

nodep->foreach<AstConstraint>([&](AstConstraint* const constrp) {
constrp->foreach<AstNodeVarRef>([&](AstNodeVarRef* const refp) {
auto* const methodp = new AstCMethodHard{
Expand Down Expand Up @@ -370,42 +374,79 @@ class RandomizeVisitor final : public VNVisitor {
}

void visit(AstMethodCall* nodep) override {
auto* classp = VN_CAST(VN_CAST(nodep->fromp(), VarRef)->dtypep(), ClassRefDType)->classp();
if (nodep->name() == "randomize" && nodep->pinsp()) {
if (nodep->pinsp()) {
auto* pinsp = nodep->pinsp()->unlinkFrBack();
VL_DO_DANGLING(pinsp->deleteTree(), pinsp);
}
/*auto* const stmtsp = ?;
if (stmtsp) {
std::string funcName = "__Vrandomize" + std::to_string(m_funcCnt++);
auto* dtypep = nodep->findBitDType(32, 32, VSigning::SIGNED);
auto* fvarp = new AstVar(fl, VVarType::MEMBER, funcName, dtypep);
fvarp->lifetime(VLifetime::AUTOMATIC);
fvarp->funcLocal(true);
fvarp->funcReturn(true);
fvarp->direction(VDirection::OUTPUT);
auto* funcp = new AstFunc(fl, funcName, nullptr, fvarp);
auto* refp = new AstFuncRef(fl, funcName, nullptr);
auto* fromp = VN_CAST(nodep->fromp(), VarRef)->varp();
funcp->addStmtsp(newClassRandStmtsp(classp, fromp));
funcp->addStmtsp(stmtsp);
funcp->dtypep(dtypep);
refp->taskp(funcp);
refp->dtypep(dtypep);
if (auto* classp = VN_CAST(m_modp, Class)) {
funcp->classMethod(true);
classp->addMembersp(funcp);
classp->repairCache();
} else {
m_modp->addStmtsp(funcp);
}
nodep->replaceWith(refp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
m_constraints = {};*/
if (!(nodep->name() == "randomize" && nodep->pinsp())) {
iterateChildren(nodep);
return;
}

VL_RESTORER(m_inMemberSel);
VL_RESTORER(m_fromp);

m_fromp = nodep->fromp();
m_inMemberSel = false;

iterateChildren(nodep);

auto* constrp = VN_AS(nodep->pinsp(), Constraint);
auto* fl = constrp->fileline();

const auto indexArgRefp = new AstLambdaArgRef{fl, "constraint__DOT__index", true};
const auto valueArgRefp = new AstLambdaArgRef{fl, "constraint", false};
indexArgRefp->dtypep(indexArgRefp->findCHandleDType());
valueArgRefp->dtypep(valueArgRefp->findBasicDType(VBasicDTypeKwd::RANDOM_GENERATOR));
const auto newp = new AstWith{fl, indexArgRefp, valueArgRefp,
constrp->condsp()->unlinkFrBackWithNext()};
newp->dtypeFrom(newp->exprp());

auto* const classp = VN_AS(nodep->fromp()->dtypep(), ClassRefDType)->classp();

iterate(classp);

auto* const genp = VN_AS(classp->findMember("constraint"), Var);
UASSERT_OBJ(genp, classp, "Randomized class without generator");
auto* const dtypep
= nodep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says int return of 0/1
const auto mathp = new AstCMath{fl, nullptr};
mathp->addExprsp(new AstText{fl, "VL_RANDOMIZE_WITH("});
mathp->addExprsp(createRef(fl, genp, nodep->fromp(), VAccess::READWRITE));
mathp->addExprsp(new AstText{fl, ", "});
mathp->addExprsp(newp);
mathp->addExprsp(new AstText{fl, ")"});
mathp->dtypep(dtypep);

nodep->replaceWith(mathp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}

void visit(AstMemberSel* nodep) override {
if (nodep->user1SetOnce()) return;
if (!m_fromp) {
iterateChildren(nodep);
return;
}

if (m_fromp->sameTree(nodep->fromp())) m_hasRandVarRef = true;
if (m_inMemberSel) {
iterateChildren(nodep);
return;
}

VL_RESTORER(m_inMemberSel);
m_inMemberSel = true;

iterateChildren(nodep);

if (m_hasRandVarRef) {
auto* const genp = new AstLambdaArgRef{m_fromp->fileline(), "constraint", false};
genp->dtypep(genp->findBasicDType(VBasicDTypeKwd::RANDOM_GENERATOR));
auto* const methodp = new AstCMethodHard{nodep->fileline(), genp, "write_var"};
methodp->dtypep(nodep->dtypep());
nodep->replaceWith(methodp);
methodp->addPinsp(nodep);
m_hasRandVarRef = false;
}
}

void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
m_modp = nodep;
Expand Down
3 changes: 1 addition & 2 deletions test_regress/t/t_randomize_method_constraints.v
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ module t (/*AUTOARG*/);
$display("===================\nSatisfiable constraints:");
for (int i = 0; i < 25; i++) begin
obj = new;
rand_result = obj.randomize();// with { 16 <= y && y <= 32; };
rand_result = obj.randomize();
$display("obj.v == %0d", obj.v);
$display("obj.w == %0d", obj.w);
$display("obj.x == %0d", obj.x);
Expand All @@ -52,7 +52,6 @@ module t (/*AUTOARG*/);
if (!(obj.v inside {ONE, THREE})) $stop;
if (obj.w != 5) $stop;
if (!(obj.x inside {1,2,4,5})) $stop;
//if (obj.y < 16 || obj.y > 32) $stop;
if (obj.z <= 13 || obj.z >= 21) $stop;
end
//$display("===================\nUnsatisfiable constraints for obj.y:");
Expand Down
21 changes: 21 additions & 0 deletions test_regress/t/t_randomize_method_with.pl
Original file line number Diff line number Diff line change
@@ -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 2020 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;
71 changes: 71 additions & 0 deletions test_regress/t/t_randomize_method_with.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0

typedef enum bit[15:0] {
ONE = 3,
TWO = 5,
THREE = 8,
FOUR = 13
} Enum;

class Cls;
constraint A { v inside {ONE, THREE}; }
constraint B { w == 5; x inside {1,2} || x inside {4,5}; }
constraint C { z < 3 * 7; z > 5 + 8; }

rand Enum v;
rand logic[63:0] w;
rand logic[47:0] x;
rand logic[31:0] y;
rand logic[23:0] z;

function new;
v = ONE;
w = 0;
x = 0;
y = 0;
z = 0;
endfunction

endclass

module t (/*AUTOARG*/);
Cls obj;

initial begin
int rand_result;
int lb, ub;
longint prev_checksum;
$display("===================\nSatisfiable constraints:");
for (int i = 0; i < 25; i++) begin
obj = new;
lb = 16;
ub = 32;
rand_result = obj.randomize() with { lb <= y && y <= ub; };
$display("obj.v == %0d", obj.v);
$display("obj.w == %0d", obj.w);
$display("obj.x == %0d", obj.x);
$display("obj.y == %0d", obj.y);
$display("obj.z == %0d", obj.z);
$display("rand_result == %0d", rand_result);
$display("-------------------");
if (!(obj.v inside {ONE, THREE})) $stop;
if (obj.w != 5) $stop;
if (!(obj.x inside {1,2,4,5})) $stop;
if (obj.y < 16 || obj.y > 32) $stop;
if (obj.z <= 13 || obj.z >= 21) $stop;
if (lb != 16 || ub != 32) $stop;
end
//$display("===================\nUnsatisfiable constraints for obj.y:");
//rand_result = obj.randomize() with { 256 < y && y < 256; };
//$display("obj.y == %0d", obj.y);
//$display("rand_result == %0d", rand_result);
//if (rand_result != 0) $stop;
//rand_result = obj.randomize() with { 16 <= z && z <= 32; };
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

0 comments on commit c975a61

Please sign in to comment.