Support randomization of scope variables with 'std::randomize()' (#5438) (#6185)

This commit is contained in:
Yilou Wang 2025-07-25 12:13:46 +02:00 committed by GitHub
parent b408e097f6
commit 10ac99ac05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 409 additions and 32 deletions

View File

@ -580,4 +580,22 @@ public:
#endif
};
//=============================================================================
// VlStdRandomizer provides a light wrapper for RNG used by std::randomize()
// to support scope-level randomization.
class VlStdRandomizer final {
// MEMBERS
VlRNG m_rng; // Random number generator
public:
// CONSTRUCTORS
VlStdRandomizer() = default;
~VlStdRandomizer() = default;
template <typename T>
bool basicStdRandomization(T& value, size_t width) {
value = VL_MASK_I(width) & VL_RANDOM_RNG_I(m_rng);
return true;
}
};
#endif // Guard

View File

@ -591,6 +591,7 @@ public:
FORK_SYNC,
PROCESS_REFERENCE,
RANDOM_GENERATOR,
RANDOM_STDGENERATOR,
// Unsigned and two state; fundamental types
UINT32,
UINT64,
@ -625,6 +626,7 @@ public:
"VlFork",
"VlProcessRef",
"VlRandomizer",
"VlStdRandomizer",
"IData",
"QData",
"LOGIC_IMPLICIT",
@ -632,13 +634,16 @@ public:
return names[m_e];
}
const char* dpiType() const {
static const char* const names[]
= {"%E-unk", "svBit", "char", "void*", "char",
"int", "%E-integer", "svLogic", "long long", "double",
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
"const char*", "%E-mtaskstate", "%E-triggervec", "%E-dly-sched", "%E-trig-sched",
"%E-dyn-sched", "%E-fork", "%E-proc-ref", "%E-rand-gen", "IData",
"QData", "%E-logic-implct", " MAX"};
static const char* const names[] = {"%E-unk", "svBit", "char",
"void*", "char", "int",
"%E-integer", "svLogic", "long long",
"double", "short", "%E-time",
"const char*", "%E-untyped", "dpiScope",
"const char*", "%E-mtaskstate", "%E-triggervec",
"%E-dly-sched", "%E-trig-sched", "%E-dyn-sched",
"%E-fork", "%E-proc-ref", "%E-rand-gen",
"%E-stdrand-gen", "IData", "QData",
"%E-logic-implct", " MAX"};
return names[m_e];
}
static void selfTest() {
@ -679,6 +684,7 @@ public:
case FORK_SYNC: return 0; // opaque
case PROCESS_REFERENCE: return 0; // opaque
case RANDOM_GENERATOR: return 0; // opaque
case RANDOM_STDGENERATOR: return 0; // opaque
case UINT32: return 32;
case UINT64: return 64;
default: return 0;
@ -718,8 +724,8 @@ public:
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|| m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR || m_e == DOUBLE
|| m_e == UNTYPED);
|| m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR
|| m_e == RANDOM_STDGENERATOR || m_e == DOUBLE || m_e == UNTYPED);
}
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
bool isEvent() const { return m_e == EVENT; }
@ -771,6 +777,8 @@ public:
/* DYNAMIC_TRIGGER_SCHEDULER: */ "", // Should not be traced
/* FORK_SYNC: */ "", // Should not be traced
/* PROCESS_REFERENCE: */ "", // Should not be traced
/* RANDOM_GENERATOR: */ "", // Should not be traced
/* RANDOM_STD_GENERATOR: */ "", // Should not be traced
/* UINT32: */ "BIT",
/* UINT64: */ "BIT",
/* LOGIC_IMPLICIT: */ "", // Should not be traced

View File

@ -473,6 +473,9 @@ public:
bool isRandomGenerator() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::RANDOM_GENERATOR;
}
bool isStdRandomGenerator() const VL_MT_SAFE {
return keyword() == VBasicDTypeKwd::RANDOM_STDGENERATOR;
}
bool isOpaque() const VL_MT_SAFE { return keyword().isOpaque(); }
bool isString() const VL_MT_STABLE { return keyword().isString(); }
bool isZeroInit() const { return keyword().isZeroInit(); }

View File

@ -1007,6 +1007,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
info.m_type = "VlProcessRef";
} else if (bdtypep->isRandomGenerator()) {
info.m_type = "VlRandomizer";
} else if (bdtypep->isStdRandomGenerator()) {
info.m_type = "VlStdRandomizer";
} else if (bdtypep->isEvent()) {
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
@ -3098,7 +3100,8 @@ void AstCMethodHard::setPurity() {
{"unique", true},
{"unique_index", true},
{"word", true},
{"write_var", false}};
{"write_var", false},
{"basicStdRandomization", false}};
if (name() == "atWriteAppend" || name() == "atWriteAppendBack") {
m_pure = false;

View File

@ -127,7 +127,8 @@ static void makeToStringMiddle(AstClass* nodep) {
if (const auto* const varp = VN_CAST(itemp, Var)) {
if (!varp->isParam() && !varp->isInternal()
&& !(varp->dtypeSkipRefp()->basicp()
&& varp->dtypeSkipRefp()->basicp()->isRandomGenerator())) {
&& (varp->dtypeSkipRefp()->basicp()->isRandomGenerator()
|| varp->dtypeSkipRefp()->basicp()->isStdRandomGenerator()))) {
string stmt = "out += \"";
stmt += comma;
comma = ", ";

View File

@ -745,7 +745,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
return "";
} else if (basicp && basicp->isDynamicTriggerScheduler()) {
return "";
} else if (basicp && basicp->isRandomGenerator()) {
} else if (basicp && (basicp->isRandomGenerator() || basicp->isStdRandomGenerator())) {
return "";
} else if (basicp) {
const bool zeroit

View File

@ -55,6 +55,7 @@ enum ClassRandom : uint8_t {
NONE, // randomize() is not called
IS_RANDOMIZED, // randomize() is called
IS_RANDOMIZED_INLINE, // randomize() with args is called
IS_STD_RANDOMIZED, // std::randomize() is called
};
// ######################################################################
@ -118,11 +119,13 @@ class RandomizeMarkVisitor final : public VNVisitor {
// NODE STATE
// Cleared on Netlist
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
// AstNodeModule::user1() -> bool. Set true to indicate needs std::randomize processing
// AstNodeExpr::user1() -> bool. Set true to indicate constraint expression depending on a
// randomized variable
// AstVar::user1() -> bool. Set true to indicate needs rand_mode
// AstVar::user2p() -> AstNodeModule*. Pointer to containing module
// AstNodeFTask::user2p() -> AstNodeModule*. Pointer to containing module
// AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
@ -367,6 +370,33 @@ class RandomizeMarkVisitor final : public VNVisitor {
if (!classp->user1()) classp->user1(IS_RANDOMIZED);
markMembers(classp);
}
if (nodep->classOrPackagep()->name() == "std") {
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
AstArg* const argp = VN_CAST(pinp, Arg);
if (!argp) continue;
AstNodeExpr* exprp = argp->exprp();
while (exprp) {
AstVar* randVarp = nullptr;
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
randVarp = memberSelp->varp();
exprp = memberSelp->fromp();
} else {
AstVarRef* const varrefp = VN_AS(exprp, VarRef);
randVarp = varrefp->varp();
exprp = nullptr;
}
UASSERT_OBJ(randVarp, nodep, "No rand variable found");
AstNode* backp = randVarp;
while (backp && (!VN_IS(backp, Class) && !VN_IS(backp, NodeModule))) {
backp = backp->backp();
}
UASSERT_OBJ(VN_IS(backp, NodeModule), randVarp,
"No class or module found for rand variable");
backp->user1(IS_STD_RANDOMIZED);
}
}
return;
}
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
AstArg* const argp = VN_CAST(pinp, Arg);
if (!argp) continue;
@ -431,6 +461,7 @@ class RandomizeMarkVisitor final : public VNVisitor {
// of type AstLambdaArgRef. They are randomized too.
const bool randObject = nodep->fromp()->user1() || VN_IS(nodep->fromp(), LambdaArgRef);
nodep->user1(randObject && nodep->varp()->rand().isRandomizable());
nodep->user2p(m_modp);
}
void visit(AstNodeModule* nodep) override {
VL_RESTORER(m_modp);
@ -472,7 +503,8 @@ public:
class ConstraintExprVisitor final : public VNVisitor {
// NODE STATE
// AstVar::user3() -> bool. Handled in constraints
// AstNodeExpr::user1() -> bool. Depending on a randomized variable
// AstNodeExpr::user1() -> bool. Depending on a randomized variable
// AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module
// VNuser3InUse m_inuser3; (Allocated for use in RandomizeVisitor)
AstNodeFTask* const m_inlineInitTaskp; // Method to add write_var calls to
@ -598,19 +630,25 @@ class ConstraintExprVisitor final : public VNVisitor {
// VISITORS
void visit(AstNodeVarRef* nodep) override {
AstVar* const varp = nodep->varp();
AstVar* varp = nodep->varp();
if (varp->user4p()) {
varp->user4p()->v3warn(
CONSTRAINTIGN,
"Size constraint combined with element constraint may not work correctly");
}
AstMemberSel* membersel = VN_IS(nodep->backp(), MemberSel)
? VN_AS(nodep->backp(), MemberSel)->cloneTree(false)
: nullptr;
if (membersel) varp = membersel->varp();
AstNodeModule* const classOrPackagep = nodep->classOrPackagep();
const RandomizeMode randMode = {.asInt = varp->user1()};
if (!randMode.usesMode && editFormat(nodep)) return;
// In SMT just variable name, but we also ensure write_var for the variable
const std::string smtName = nodep->name(); // Can be anything unique
const std::string smtName = membersel
? membersel->fromp()->name() + "." + membersel->name()
: nodep->name(); // Can be anything unique
VNRelinker relinker;
nodep->unlinkFrBack(&relinker);
AstNodeExpr* exprp = new AstSFormatF{nodep->fileline(), smtName, false, nullptr};
@ -648,11 +686,12 @@ class ConstraintExprVisitor final : public VNVisitor {
dimension = 1;
}
methodp->dtypeSetVoid();
AstClass* const classp = VN_AS(varp->user2p(), Class);
AstClass* const classp
= membersel ? VN_AS(membersel->user2p(), Class) : VN_AS(varp->user2p(), Class);
AstVarRef* const varRefp
= new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE};
varRefp->classOrPackagep(classOrPackagep);
methodp->addPinsp(varRefp);
membersel ? methodp->addPinsp(membersel) : methodp->addPinsp(varRefp);
AstNodeDType* tmpDtypep = varp->dtypep();
while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType)
|| VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType))
@ -838,6 +877,12 @@ class ConstraintExprVisitor final : public VNVisitor {
if (nodep->user1()) {
nodep->v3warn(CONSTRAINTIGN, "Global constraints ignored (unsupported)");
}
if (VN_IS(nodep->fromp(), NodeVarRef) && nodep->varp()->isRand() && m_inlineInitTaskp) {
iterateChildren(nodep);
nodep->replaceWith(nodep->fromp()->unlinkFrBack());
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
}
editFormat(nodep);
}
void visit(AstSFormatF* nodep) override {}
@ -1121,9 +1166,11 @@ class CaptureVisitor final : public VNVisitor {
void captureRefByThis(AstNodeVarRef* nodep, CaptureMode capModeFlags) {
AstVar* const thisp = importThisp(nodep->fileline());
AstVarRef* const thisRefp = new AstVarRef{nodep->fileline(), thisp, nodep->access()};
thisRefp->user1(true);
m_ignore.emplace(thisRefp);
AstMemberSel* const memberSelp
= new AstMemberSel(nodep->fileline(), thisRefp, nodep->varp());
memberSelp->user2p(m_targetp);
nodep->replaceWith(memberSelp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
m_ignore.emplace(memberSelp);
@ -1249,6 +1296,7 @@ class RandomizeVisitor final : public VNVisitor {
// AstConstraint::user3p() -> AstTask*. Pointer to resize procedure
// AstClass::user4p() -> AstVar*. Constraint mode state variable
// AstVar::user4p() -> AstVar*. Size variable for constrained queues
// AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module
// VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor)
// VNUser2InUse m_inuser2; (Allocated for use in RandomizeMarkVisitor)
const VNUser3InUse m_inuser3;
@ -1258,8 +1306,10 @@ class RandomizeVisitor final : public VNVisitor {
V3UniqueNames m_inlineUniqueNames; // For generating unique function names
V3UniqueNames m_modeUniqueNames{"__Vmode"}; // For generating unique rand/constraint
// mode state var names
V3UniqueNames m_inlineUniqueStdName{"__VStdrand"};
VMemberMap m_memberMap; // Member names cached for fast lookup
AstNodeModule* m_modp = nullptr; // Current module
std::unordered_map<AstNodeModule*, AstVar*> m_stdMap; // Map from module/class to AST Var
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
AstNodeStmt* m_stmtp = nullptr; // Current statement
AstDynArrayDType* m_dynarrayDtp = nullptr; // Dynamic array type (for rand mode)
@ -1281,6 +1331,18 @@ class RandomizeVisitor final : public VNVisitor {
classp->addMembersp(genp);
classp->user3p(genp);
}
AstVar* createStdRandomGenerator(AstNodeModule* const modp) {
auto it = m_stdMap.find(modp);
if (it == m_stdMap.end()) {
AstVar* const stdgenp
= new AstVar{modp->fileline(), VVarType::MEMBER, "stdrand",
modp->findBasicDType(VBasicDTypeKwd::RANDOM_STDGENERATOR)};
modp->addStmtsp(stdgenp);
m_stdMap.emplace(modp, stdgenp);
return stdgenp;
}
return it->second;
}
AstVar* getRandomGenerator(AstClass* const classp) {
if (classp->user3p()) return VN_AS(classp->user3p(), Var);
if (classp->extendsp()) return getRandomGenerator(classp->extendsp()->classp());
@ -2184,16 +2246,53 @@ class RandomizeVisitor final : public VNVisitor {
if (nodep->name() != "randomize") return;
if (nodep->classOrPackagep() && nodep->classOrPackagep()->name() == "std") {
AstVar* const stdrand = createStdRandomGenerator(m_modp);
AstFunc* const randomizeFuncp = V3Randomize::newRandomizeStdFunc(
m_memberMap, m_modp, m_inlineUniqueStdName.get(nodep));
randomizeFuncp->addStmtsp(
new AstAssign{nodep->fileline(),
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var),
VAccess::WRITE},
new AstConst{nodep->fileline(), AstConst::WidthedValue{}, 32, 1}});
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
AstArg* const argp = VN_CAST(pinp, Arg);
if (!argp) continue;
AstNodeExpr* exprp = argp->exprp();
AstCMethodHard* const basicMethodp = new AstCMethodHard{
nodep->fileline(),
new AstVarRef{nodep->fileline(), stdrand, VAccess::READWRITE},
"basicStdRandomization"};
const size_t width = exprp->width();
basicMethodp->addPinsp(exprp->unlinkFrBack());
basicMethodp->addPinsp(
new AstConst{nodep->fileline(), AstConst::Unsized64{}, width});
basicMethodp->dtypeSetBit();
randomizeFuncp->addStmtsp(new AstAssign{
nodep->fileline(),
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var),
VAccess::WRITE},
new AstAnd{nodep->fileline(),
new AstVarRef{nodep->fileline(),
VN_AS(randomizeFuncp->fvarp(), Var), VAccess::READ},
basicMethodp}});
}
// Replace the node with a call to that function
nodep->name(randomizeFuncp->name());
nodep->taskp(randomizeFuncp);
nodep->dtypeFrom(randomizeFuncp->dtypep());
if (VN_IS(m_modp, Class)) nodep->classOrPackagep(m_modp);
if (nodep->pinsp()) pushDeletep(nodep->pinsp()->unlinkFrBackWithNext());
return;
}
handleRandomizeArgs(nodep);
AstWith* const withp = VN_CAST(nodep->pinsp(), With);
if (!withp) {
iterateChildren(nodep);
return;
}
withp->unlinkFrBack();
iterateChildren(nodep);
AstClass* classp = nullptr;
@ -2232,7 +2331,6 @@ class RandomizeVisitor final : public VNVisitor {
// Detach the expression and prepare variable copies
const CaptureVisitor captured{withp->exprp(), m_modp, classp};
// Add function arguments
captured.addFunctionArguments(randomizeFuncp);
@ -2406,6 +2504,28 @@ AstFunc* V3Randomize::newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep,
return funcp;
}
AstFunc* V3Randomize::newRandomizeStdFunc(VMemberMap& memberMap, AstNodeModule* nodep,
const std::string& name) {
AstFunc* funcp = nullptr;
v3Global.useRandomizeMethods(true);
AstNodeDType* const dtypep = nodep->findBitDType(32, 32, VSigning::SIGNED);
AstVar* const fvarp = new AstVar{nodep->fileline(), VVarType::MEMBER, name, dtypep};
fvarp->lifetime(VLifetime::AUTOMATIC);
fvarp->funcLocal(true);
fvarp->funcReturn(true);
fvarp->direction(VDirection::OUTPUT);
funcp = new AstFunc{nodep->fileline(), name, nullptr, fvarp};
funcp->dtypep(dtypep);
if (VN_IS(nodep, Class)) {
funcp->classMethod(true);
} else {
funcp->classMethod(false);
funcp->isStatic(true);
}
nodep->addStmtsp(funcp);
return funcp;
}
AstFunc* V3Randomize::newSRandomFunc(VMemberMap& memberMap, AstClass* nodep) {
AstClass* const basep = nodep->baseMostClassp();
AstFunc* funcp = VN_AS(memberMap.findMember(basep, "srandom"), Func);

View File

@ -23,6 +23,7 @@
class AstClass;
class AstFunc;
class AstNetlist;
class AstNodeModule;
class VMemberMap;
@ -34,6 +35,8 @@ public:
const std::string& name = "randomize",
bool allowVirtual = true,
bool childDType = false) VL_MT_DISABLED;
static AstFunc* newRandomizeStdFunc(VMemberMap& memberMap, AstNodeModule* nodep,
const std::string& name) VL_MT_DISABLED;
static AstFunc* newSRandomFunc(VMemberMap& memberMap, AstClass* nodep) VL_MT_DISABLED;
};

View File

@ -6282,9 +6282,39 @@ class WidthVisitor final : public VNVisitor {
}
}
}
void handleStdRandomizeArgs(AstNodeFTaskRef* const nodep) {
AstConst* nullp = nullptr;
for (AstNode *pinp = nodep->pinsp(), *nextp = nullptr; pinp; pinp = nextp) {
nextp = pinp->nextp();
AstArg* const argp = VN_CAST(pinp, Arg);
if (!argp) continue;
AstNodeExpr* const exprp = argp->exprp();
if (AstConst* const constp = VN_CAST(exprp, Const)) {
if (constp->num().isNull()) {
nullp = constp;
continue;
}
}
if (VN_IS(exprp, MemberSel)) {
// Non-standard usage: std::randomize() with class-scoped member
// IEEE 1800-2023 (18.12) limits args to current scope variables.
// Verilator accepts this for compatibility with other simulators.
continue;
} else if (VN_IS(exprp, VarRef)) {
// Valid usage
continue;
} else {
argp->v3error("Non-variable arguments for 'std::randomize()'.");
}
if (!argp) continue;
}
if (nullp) { nullp->v3error("'std::randomize()' does not accept 'null' as arguments."); }
}
void visit(AstNodeFTaskRef* nodep) override {
// For arguments, is assignment-like context; see IEEE rules in AstNodeAssign
// Function hasn't been widthed, so make it so.
if (nodep->didWidth()) return;
UINFO(5, " FTASKREF " << nodep);
AstWith* withp = nullptr;
if (nodep->name() == "rand_mode" || nodep->name() == "constraint_mode") {
@ -6297,16 +6327,23 @@ class WidthVisitor final : public VNVisitor {
|| nodep->name() == "set_randstate"))) {
// TODO perhaps this should move to V3LinkDot
AstClass* const classp = VN_CAST(nodep->classOrPackagep(), Class);
if (!classp) {
if (nodep->classOrPackagep()->name() == "std") {
v3Global.useRandomizeMethods(true);
AstNodeDType* const adtypep = nodep->findBitDType();
withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(),
adtypep->findBitDType(), adtypep);
for (const AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp())
userIterateAndNext(VN_AS(argp, Arg)->exprp(), WidthVP{SELF, BOTH}.p());
handleStdRandomizeArgs(nodep); // Provided args should be in current scope
if (withp) {
nodep->v3warn(CONSTRAINTIGN, "Unsupported: std::randomize()'s 'with'");
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
processFTaskRefArgs(nodep);
nodep->addPinsp(withp);
nodep->v3warn(CONSTRAINTIGN, "std::randomize ignored (unsupported)");
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
nodep->didWidth(true);
return;
}
UASSERT_OBJ(classp, nodep, "Should have failed in V3LinkDot");

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 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')
test.lint(verilator_flags2=["--binary"])
test.passes()

View File

@ -0,0 +1,81 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by PlanV GmbH.
// SPDX-License-Identifier: CC0-1.0
class std_randomize_class;
rand bit [7:0] addr;
rand bit [31:0] data;
rand bit [63:0] data_x_4;
bit [7:0] old_addr;
bit [31:0] old_data;
bit [63:0] old_data_x_4;
function bit std_randomize();
int success;
bit valid;
old_addr = addr;
old_data = data;
old_data_x_4 = data_x_4;
success = std::randomize(addr, data);
valid = (success == 1) && !(addr == old_addr || data == old_data) && data_x_4 == old_data_x_4;
return valid;
endfunction
endclass
module t_scope_std_randomize;
bit [7:0] addr;
bit [15:0] data;
function bit run();
int ready;
int success;
bit [7:0] old_addr;
bit [15:0] old_data;
int old_ready;
old_addr = addr;
old_data = data;
old_ready = ready;
success = randomize(addr, ready); // std::randomize
if (success == 0) return 0;
if (addr == old_addr && data != old_data && ready == old_ready) begin
return 0;
end
return 1;
endfunction
std_randomize_class test;
initial begin
bit ok = 0;
int success;
test = new();
test.old_addr = test.addr;
test.old_data = test.data;
test.old_data_x_4 = test.data_x_4;
success = std::randomize(test.addr, test.data);
ok = (success == 1) && !(test.addr == test.old_addr || test.data == test.old_data) && test.data_x_4 == test.old_data_x_4;
if (!ok) $stop;
ok = 0;
ok = run();
if (!ok) $stop;
ok = 0;
ok = test.std_randomize();
if (!ok) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,10 @@
%Error: t/t_std_randomize_bad1.v:12:36: Non-variable arguments for 'std::randomize()'.
: ... note: In instance 't_std_randomize_bad1'
12 | success = std::randomize(a + 1);
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: t/t_std_randomize_bad1.v:19:30: 'std::randomize()' does not accept 'null' as arguments.
: ... note: In instance 't_std_randomize_bad1'
19 | void'(std::randomize(null));
| ^~~~
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 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=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,23 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by PlanV GmbH.
// SPDX-License-Identifier: CC0-1.0
module t_std_randomize_bad1;
bit [3:0] a;
function int run();
int success;
success = std::randomize(a + 1); // ERROR: argument is not a variable
return success;
endfunction
initial begin
int ok, x;
ok = run();
void'(std::randomize(null));
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 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')
test.lint(verilator_flags2=["--binary"])
test.passes()

View File

@ -0,0 +1,26 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by PlanV GmbH.
// SPDX-License-Identifier: CC0-1.0
module t_no_args;
bit [7:0] addr;
bit [15:0] data;
bit [7:0] old_addr;
bit [15:0] old_data;
int success;
bit valid;
initial begin
old_addr = addr;
old_data = data;
success = std::randomize();
valid = (success == 1) && (addr == old_addr) && (data == old_data);
if (!valid) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,11 +1,7 @@
%Warning-CONSTRAINTIGN: t/t_std_randomize_unsup_bad.v:10:16: std::randomize ignored (unsupported)
: ... note: In instance 't'
10 | if (std::randomize(a, b) != 1) $stop;
| ^~~~~~~~~
... 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_std_randomize_unsup_bad.v:11:16: std::randomize ignored (unsupported)
%Warning-CONSTRAINTIGN: t/t_std_randomize_unsup_bad.v:11:16: Unsupported: std::randomize()'s 'with'
: ... note: In instance 't'
11 | if (std::randomize(a, b) with { 2 < a; a < 7; b < a; } != 1) $stop;
| ^~~~~~~~~
... 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.
%Error: Exiting due to