parent
93c594e18a
commit
76c1b26e3b
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "verilated_random.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
|
@ -330,6 +331,30 @@ void VlRandomVar::emitExtract(std::ostream& s, int i) const {
|
|||
s << " ((_ extract " << i << ' ' << i << ") " << m_name << ')';
|
||||
}
|
||||
void VlRandomVar::emitType(std::ostream& s) const { s << "(_ BitVec " << width() << ')'; }
|
||||
// Serialize the current runtime value as an SMT-LIB binary literal. Used by
|
||||
// randomize(null) to pin a var via `(assert (= var #b...))`. Binary (#b)
|
||||
// rather than hex (#x) sidesteps SMT-LIB's hex-width-multiple-of-4 rule.
|
||||
void VlRandomVar::emitConcreteValue(std::ostream& s) const {
|
||||
const int w = width();
|
||||
const void* const dp = datap(0);
|
||||
s << "#b";
|
||||
for (int i = w - 1; i >= 0; --i) {
|
||||
int bit = 0;
|
||||
if (w <= VL_BYTESIZE) {
|
||||
bit = (*static_cast<const CData*>(dp) >> i) & 1;
|
||||
} else if (w <= VL_SHORTSIZE) {
|
||||
bit = (*static_cast<const SData*>(dp) >> i) & 1;
|
||||
} else if (w <= VL_IDATASIZE) {
|
||||
bit = (*static_cast<const IData*>(dp) >> i) & 1;
|
||||
} else if (w <= VL_QUADSIZE) {
|
||||
bit = (*static_cast<const QData*>(dp) >> i) & 1;
|
||||
} else {
|
||||
const EData* const wp = static_cast<const EData*>(dp);
|
||||
bit = (wp[VL_BITWORD_E(i)] >> VL_BITBIT_E(i)) & 1;
|
||||
}
|
||||
s << (bit ? '1' : '0');
|
||||
}
|
||||
}
|
||||
int VlRandomVar::totalWidth() const { return m_width; }
|
||||
static bool parseSMTNum(int obits, WDataOutP owp, const std::string& val) {
|
||||
int i;
|
||||
|
|
@ -441,8 +466,16 @@ void VlRandomizer::recordRandcValues() {
|
|||
}
|
||||
}
|
||||
|
||||
bool VlRandomizer::next_check_only(VlRNG& rngr) {
|
||||
m_checkOnly = true;
|
||||
const bool result = next(rngr);
|
||||
m_checkOnly = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VlRandomizer::next(VlRNG& rngr) {
|
||||
if (m_vars.empty() && m_unique_arrays.empty()) return true;
|
||||
if (!m_checkOnly && m_vars.empty() && m_unique_arrays.empty()) return true;
|
||||
if (m_checkOnly && m_vars.empty()) return true; // No rand members: trivially SAT
|
||||
for (const std::string& baseName : m_unique_arrays) {
|
||||
const auto it = m_vars.find(baseName);
|
||||
const uint32_t size = m_unique_array_sizes.at(baseName);
|
||||
|
|
@ -470,8 +503,8 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
|||
}
|
||||
}
|
||||
|
||||
// If solve-before constraints are present, use phased solving
|
||||
if (!m_solveBefore.empty()) return nextPhased(rngr);
|
||||
// Pinned vars make phase ordering moot; skip phased path in check-only.
|
||||
if (!m_checkOnly && !m_solveBefore.empty()) return nextPhased(rngr);
|
||||
|
||||
// Randc retry: if unsat due to randc exhaustion, clear history and retry once
|
||||
const bool hasRandc = !m_randcVarNames.empty();
|
||||
|
|
@ -494,14 +527,24 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
|||
os << "(declare-fun " << var.first << " () ";
|
||||
var.second->emitType(os);
|
||||
os << ")\n";
|
||||
// Pin each var to its current value: SAT iff the current values
|
||||
// satisfy the constraints. V3Randomize rejects non-scalar rand
|
||||
// members upstream, hence the assert.
|
||||
if (m_checkOnly) {
|
||||
assert(var.second->dimension() == 0);
|
||||
os << "(assert (= " << var.first << ' ';
|
||||
var.second->emitConcreteValue(os);
|
||||
os << "))\n";
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& constraint : m_constraints) {
|
||||
os << "(assert (= #b1 " << constraint << "))\n";
|
||||
}
|
||||
|
||||
// Randc: exclude previously used values to enforce cyclic non-repetition
|
||||
emitRandcExclusions(os);
|
||||
// randc exclusions vs. a pinned current value would make every check
|
||||
// trivially UNSAT after the first cycle.
|
||||
if (!m_checkOnly) emitRandcExclusions(os);
|
||||
|
||||
const size_t nSoft = m_softConstraints.size();
|
||||
bool sat = false;
|
||||
|
|
@ -544,6 +587,10 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
|||
m_randcUsedValues.clear();
|
||||
continue; // Retry without exclusions
|
||||
}
|
||||
// Skip the unsat-core path in check-only: it re-declares vars
|
||||
// without pinning, so parseSolution would clobber user state with
|
||||
// the solver's free assignment.
|
||||
if (m_checkOnly) return false;
|
||||
// Genuine unsat: report via unsat-core
|
||||
os << "(set-option :produce-unsat-cores true)\n";
|
||||
os << "(set-logic QF_ABV)\n";
|
||||
|
|
@ -569,17 +616,20 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
|||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
|
||||
os << "(assert ";
|
||||
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
|
||||
os << ")\n";
|
||||
os << "\n(check-sat)\n";
|
||||
sat = parseSolution(os, false);
|
||||
(void)sat;
|
||||
// Pinned vars: salting cannot diversify, only burn solver calls.
|
||||
if (!m_checkOnly) {
|
||||
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
|
||||
os << "(assert ";
|
||||
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
|
||||
os << ")\n";
|
||||
os << "\n(check-sat)\n";
|
||||
sat = parseSolution(os, false);
|
||||
(void)sat;
|
||||
}
|
||||
}
|
||||
|
||||
// Record solved randc values for future exclusion
|
||||
recordRandcValues();
|
||||
// Check-only must not advance randc cycle state.
|
||||
if (!m_checkOnly) recordRandcValues();
|
||||
|
||||
os << "(reset)\n";
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -82,6 +82,9 @@ public:
|
|||
virtual void emitGetValue(std::ostream& s) const;
|
||||
virtual void emitExtract(std::ostream& s, int i) const;
|
||||
virtual void emitType(std::ostream& s) const;
|
||||
// Emit the current runtime value as an SMT bit-vector literal (#b...).
|
||||
// Used by randomize(null) to pin a var to its existing value.
|
||||
virtual void emitConcreteValue(std::ostream& s) const;
|
||||
virtual int totalWidth() const;
|
||||
mutable std::shared_ptr<const ArrayInfoMap> m_arrVarsRefp;
|
||||
void setArrayInfo(const std::shared_ptr<const ArrayInfoMap>& arrVarsRefp) const {
|
||||
|
|
@ -223,6 +226,7 @@ class VlRandomizer VL_NOT_FINAL {
|
|||
size_t m_randcConstraintHash = 0; // Hash of constraints when history was valid
|
||||
std::vector<std::pair<std::string, std::string>>
|
||||
m_solveBefore; // Solve-before ordering pairs (beforeVar, afterVar)
|
||||
bool m_checkOnly = false; // Set for randomize(null)
|
||||
|
||||
// PRIVATE METHODS
|
||||
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
|
||||
|
|
@ -241,6 +245,9 @@ public:
|
|||
// METHODS
|
||||
// Finds the next solution satisfying the constraints
|
||||
bool next(VlRNG& rngr);
|
||||
// Validate the constraints against the current runtime values of every
|
||||
// registered rand variable without picking new ones.
|
||||
bool next_check_only(VlRNG& rngr);
|
||||
|
||||
// --- Process the key for associative array ---
|
||||
|
||||
|
|
|
|||
|
|
@ -607,6 +607,28 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
return;
|
||||
}
|
||||
for (AstArg* argp = nodep->argsp(); argp; argp = VN_AS(argp->nextp(), Arg)) {
|
||||
// randomize(null): handle before IS_RANDOMIZED_INLINE so the
|
||||
// check-only path does not allocate unused __Vrandmode slots.
|
||||
if (const AstConst* const constp = VN_CAST(argp->exprp(), Const)) {
|
||||
UASSERT_OBJ(constp->num().isNull(), constp,
|
||||
"Non-null AstConst arg to randomize() should have been "
|
||||
"rejected by V3Width");
|
||||
// SMT pin only handles scalars; nested-class constraints don't cascade.
|
||||
const bool hasUnsupportedMember
|
||||
= classp->existsMember([](const AstClass*, const AstVar* memberVarp) {
|
||||
if (!memberVarp->rand().isRandomizable()) return false;
|
||||
const AstNodeDType* const dtp = memberVarp->dtypep()->skipRefp();
|
||||
return VN_IS(dtp, UnpackArrayDType) || VN_IS(dtp, DynArrayDType)
|
||||
|| VN_IS(dtp, QueueDType) || VN_IS(dtp, AssocArrayDType)
|
||||
|| VN_IS(dtp, WildcardArrayDType) || VN_IS(dtp, ClassRefDType);
|
||||
});
|
||||
if (hasUnsupportedMember) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: 'randomize(null)' on class with rand "
|
||||
"container or class member");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
classp->user1(IS_RANDOMIZED_INLINE);
|
||||
AstVar* fromVarp = nullptr; // If nodep is a method call, this is its receiver
|
||||
if (AstMethodCall* methodCallp = VN_CAST(nodep, MethodCall)) {
|
||||
|
|
@ -4027,6 +4049,19 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
// Handle inline random variable control. After this, the randomize() call has no args
|
||||
void handleRandomizeArgs(AstNodeFTaskRef* const nodep) {
|
||||
if (!nodep->argsp()) return;
|
||||
// Strip the null literal arg. V3Width already rejected mixed/non-null
|
||||
// AstConst args.
|
||||
bool hasNullArg = false;
|
||||
for (AstArg *argp = nodep->argsp(), *nextp = nullptr; argp; argp = nextp) {
|
||||
nextp = VN_AS(argp->nextp(), Arg);
|
||||
if (const AstConst* const constp = VN_CAST(argp->exprp(), Const)) {
|
||||
UASSERT_OBJ(constp->num().isNull(), constp,
|
||||
"Non-null AstConst arg to randomize() should have been "
|
||||
"rejected by V3Width");
|
||||
hasNullArg = true;
|
||||
VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
|
||||
}
|
||||
}
|
||||
// This assumes arguments to always be a member sel from nodep->fromp(), if applicable
|
||||
// e.g. LinkDot transformed a.randomize(b, a.c) -> a.randomize(a.b, a.c)
|
||||
// Merge pins with common prefixes so that setting their rand mode doesn't interfere
|
||||
|
|
@ -4089,6 +4124,24 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
}
|
||||
argp->unlinkFrBack()->deleteTree();
|
||||
}
|
||||
if (hasNullArg) { // Re-point to the per-class __Vrandomize_null wrapper
|
||||
AstClass* targetClassp = nullptr;
|
||||
AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall);
|
||||
if (methodCallp) {
|
||||
const AstNodeDType* const fromDTypep = methodCallp->fromp()->dtypep();
|
||||
const AstClassRefDType* const crdtp
|
||||
= VN_CAST(fromDTypep->skipRefp(), ClassRefDType);
|
||||
UASSERT_OBJ(crdtp, nodep, "randomize(null) receiver is not a class type");
|
||||
targetClassp = crdtp->classp();
|
||||
} else {
|
||||
targetClassp = VN_CAST(m_modp, Class);
|
||||
}
|
||||
UASSERT_OBJ(targetClassp, nodep, "randomize(null) target class unresolved");
|
||||
AstFunc* const checkOnlyFuncp = getCreateRandomizeNullFunc(targetClassp);
|
||||
nodep->name(checkOnlyFuncp->name());
|
||||
nodep->taskp(checkOnlyFuncp);
|
||||
nodep->dtypeFrom(checkOnlyFuncp->dtypep());
|
||||
}
|
||||
if (tmpVarps) {
|
||||
UASSERT_OBJ(storeStmtsp && setStmtsp && restoreStmtsp, nodep, "Should have stmts");
|
||||
VNRelinker relinker;
|
||||
|
|
@ -4102,6 +4155,58 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
// Create a class method `__Vrandomize_null` that implements the IEEE
|
||||
// 1800-2023 18.11 semantic: validate all declared constraints against the
|
||||
// current runtime values without assigning new ones. Body is:
|
||||
// 1. pre_randomize() -- always (IEEE 1800-2023 18.6.2; 18.11 has no
|
||||
// carve-out for the null case).
|
||||
// 2. Compute result:
|
||||
// - no constraints: `fvar = 1` (trivially satisfied; IEEE 18.11.1).
|
||||
// - has constraints: clear solver constraints, re-run
|
||||
// `__Vsetup_constraints`, then `fvar = gen.next_check_only(rng)`.
|
||||
// 3. if (fvar) post_randomize() -- IEEE 1800-2023 18.6.3 says
|
||||
// post_randomize is not called when randomize() fails.
|
||||
AstFunc* getCreateRandomizeNullFunc(AstClass* const classp) {
|
||||
static const char* const name = "__Vrandomize_null";
|
||||
if (AstFunc* const existingp = VN_AS(m_memberMap.findMember(classp, name), Func)) {
|
||||
return existingp;
|
||||
}
|
||||
FileLine* const fl = classp->fileline();
|
||||
AstFunc* const funcp = V3Randomize::newRandomizeFunc(m_memberMap, classp, name);
|
||||
AstVar* const fvarp = VN_AS(funcp->fvarp(), Var);
|
||||
|
||||
// 1. pre_randomize -- always
|
||||
addPrePostCall(classp, funcp, "pre_randomize");
|
||||
|
||||
// 2. Compute result
|
||||
AstVar* const classGenp = getRandomGenerator(classp);
|
||||
if (!classGenp) {
|
||||
funcp->addStmtsp(new AstAssign{fl, new AstVarRef{fl, fvarp, VAccess::WRITE},
|
||||
new AstConst{fl, AstConst::WidthedValue{}, 32, 1}});
|
||||
} else {
|
||||
AstNodeModule* const genModp = VN_AS(classGenp->user2p(), NodeModule);
|
||||
funcp->addStmtsp(implementConstraintsClear(fl, classGenp));
|
||||
AstTask* const setupAllTaskp = getCreateConstraintSetupFunc(classp);
|
||||
funcp->addStmtsp((new AstTaskRef{fl, setupAllTaskp})->makeStmt());
|
||||
AstCExpr* const solverCallp = new AstCExpr{fl};
|
||||
solverCallp->dtypeSetBit();
|
||||
solverCallp->add(new AstVarRef{fl, genModp, classGenp, VAccess::READWRITE});
|
||||
solverCallp->add(".next_check_only(__Vm_rng)");
|
||||
funcp->addStmtsp(
|
||||
new AstAssign{fl, new AstVarRef{fl, fvarp, VAccess::WRITE}, solverCallp});
|
||||
classp->needRNG(true);
|
||||
}
|
||||
|
||||
// 3. post_randomize -- only if result is non-zero
|
||||
if (AstTask* const userPostp = findPrePostTask(classp, "post_randomize")) {
|
||||
AstTaskRef* const callp = new AstTaskRef{userPostp->fileline(), userPostp};
|
||||
funcp->addStmtsp(
|
||||
new AstIf{fl, new AstVarRef{fl, fvarp, VAccess::READ}, callp->makeStmt()});
|
||||
}
|
||||
|
||||
return funcp;
|
||||
}
|
||||
|
||||
// Rewrite a LogIf-of-Dist chain into nested AstConstraintIf. The outermost
|
||||
// AstLogIf shell is left for the caller's AstConstraintExpr to free; inner
|
||||
// shells are deleted here once their children are transplanted.
|
||||
|
|
|
|||
|
|
@ -4740,13 +4740,12 @@ class WidthVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp);
|
||||
}
|
||||
}
|
||||
if (nullp) {
|
||||
if (hasNonNullArgs) {
|
||||
nullp->v3error("Cannot pass more arguments to 'randomize(null)'");
|
||||
} else {
|
||||
nullp->v3warn(E_UNSUPPORTED, "Unsupported: 'randomize(null)'");
|
||||
}
|
||||
if (nullp && hasNonNullArgs) {
|
||||
nullp->v3error("Cannot pass more arguments to 'randomize(null)'");
|
||||
}
|
||||
// A solo 'null' arg is left in place; V3Randomize lowers it into a
|
||||
// check-only solve so constraints are validated against the current
|
||||
// values of every rand member (IEEE 1800-2023 18.11).
|
||||
}
|
||||
void methodCallClass(AstMethodCall* nodep, AstClassRefDType* adtypep) {
|
||||
// No need to width-resolve the class, as it was done when we did the child
|
||||
|
|
|
|||
|
|
@ -7,8 +7,4 @@
|
|||
: ... note: In instance 't'
|
||||
21 | void'(foo.randomize(foos[0].x));
|
||||
| ^
|
||||
%Error-UNSUPPORTED: t/t_randomize_inline_var_ctl_unsup_1.v:22:25: Unsupported: 'randomize(null)'
|
||||
: ... note: In instance 't'
|
||||
22 | void'(foo.randomize(null));
|
||||
| ^~~~
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
#!/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: 2026 Wilson Snyder
|
||||
# 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,251 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
class A;
|
||||
rand int x;
|
||||
int v;
|
||||
constraint c_lt {x < v;}
|
||||
endclass
|
||||
|
||||
class Multi;
|
||||
rand int x;
|
||||
rand int y;
|
||||
int lo;
|
||||
int hi;
|
||||
constraint c_x {x >= lo; x <= hi;}
|
||||
constraint c_y {y > x;}
|
||||
|
||||
function int self_check;
|
||||
return this.randomize(null);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Trivial;
|
||||
rand int p;
|
||||
rand bit [3:0] q;
|
||||
endclass
|
||||
|
||||
class Base;
|
||||
rand int x;
|
||||
int v;
|
||||
constraint c_base {x <= v;}
|
||||
endclass
|
||||
|
||||
class Derived extends Base;
|
||||
rand int y;
|
||||
constraint c_derived {y > x;}
|
||||
endclass
|
||||
|
||||
class Wide;
|
||||
rand bit [64:0] w65;
|
||||
rand bit [95:0] w96;
|
||||
bit [64:0] lo65;
|
||||
bit [95:0] lo96;
|
||||
constraint c_wide {w65 >= lo65; w96 >= lo96;}
|
||||
endclass
|
||||
|
||||
// Cover the 16-bit (SData) and 64-bit (QData) tiers of
|
||||
// VlRandomVar::emitConcreteValue's bit-extraction ladder, exercised when
|
||||
// pinned current values are serialized to the SMT solver.
|
||||
class Widths;
|
||||
rand shortint s16;
|
||||
rand longint l64;
|
||||
shortint s_lo;
|
||||
longint l_lo;
|
||||
constraint c_widths {s16 >= s_lo; l64 >= l_lo;}
|
||||
endclass
|
||||
|
||||
class Cyc;
|
||||
randc bit [1:0] c;
|
||||
bit [1:0] lo;
|
||||
constraint c_range {c >= lo;}
|
||||
endclass
|
||||
|
||||
// IEEE 1800-2023 18.6.2 / 18.6.3: pre_randomize is always called; post_randomize
|
||||
// is called iff randomize() returned 1.
|
||||
class Cb;
|
||||
rand int x;
|
||||
int v;
|
||||
int pre_count;
|
||||
int post_count;
|
||||
constraint c_lt {x < v;}
|
||||
function void pre_randomize; pre_count = pre_count + 1; endfunction
|
||||
function void post_randomize; post_count = post_count + 1; endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
A a;
|
||||
Multi m;
|
||||
Trivial triv;
|
||||
Derived d;
|
||||
Wide w;
|
||||
Widths wd;
|
||||
Cyc cyc;
|
||||
Cb cb;
|
||||
int i;
|
||||
int ok0;
|
||||
int ok1;
|
||||
|
||||
initial begin
|
||||
// 1. Original issue reproducer: unsat keeps values, sat preserves them.
|
||||
a = new;
|
||||
a.x = 2; a.v = 1;
|
||||
i = a.randomize(null);
|
||||
`checkd(i, 0);
|
||||
`checkd(a.x, 2);
|
||||
`checkd(a.v, 1);
|
||||
|
||||
a.x = 1; a.v = 2;
|
||||
i = a.randomize(null);
|
||||
`checkd(i, 1);
|
||||
`checkd(a.x, 1);
|
||||
`checkd(a.v, 2);
|
||||
|
||||
// 2. Multiple rand members, multiple constraints, plus implicit-this path.
|
||||
m = new;
|
||||
m.x = 5; m.y = 7; m.lo = 0; m.hi = 10;
|
||||
i = m.randomize(null);
|
||||
`checkd(i, 1);
|
||||
`checkd(m.x, 5);
|
||||
`checkd(m.y, 7);
|
||||
|
||||
m.x = -1; m.y = 7; m.lo = 0; m.hi = 10;
|
||||
i = m.randomize(null);
|
||||
`checkd(i, 0);
|
||||
`checkd(m.x, -1);
|
||||
|
||||
m.x = 5; m.y = 5; m.lo = 0; m.hi = 10;
|
||||
i = m.randomize(null);
|
||||
`checkd(i, 0);
|
||||
`checkd(m.y, 5);
|
||||
|
||||
m.x = 3; m.y = 9; m.lo = 0; m.hi = 10;
|
||||
i = m.self_check();
|
||||
`checkd(i, 1);
|
||||
|
||||
// 3. Class with rand vars and no constraints: always sat, values untouched.
|
||||
triv = new;
|
||||
triv.p = 42;
|
||||
triv.q = 4'h5;
|
||||
i = triv.randomize(null);
|
||||
`checkd(i, 1);
|
||||
`checkd(triv.p, 42);
|
||||
`checkh(triv.q, 4'h5);
|
||||
|
||||
// 4. Inheritance: base and derived constraints validated together.
|
||||
d = new;
|
||||
d.x = 2; d.y = 5; d.v = 10;
|
||||
i = d.randomize(null);
|
||||
`checkd(i, 1);
|
||||
`checkd(d.x, 2);
|
||||
`checkd(d.y, 5);
|
||||
|
||||
d.x = 11; d.y = 20; d.v = 10;
|
||||
i = d.randomize(null);
|
||||
`checkd(i, 0);
|
||||
`checkd(d.x, 11);
|
||||
|
||||
d.x = 3; d.y = 1; d.v = 10;
|
||||
i = d.randomize(null);
|
||||
`checkd(i, 0);
|
||||
`checkd(d.y, 1);
|
||||
|
||||
// 5. Wide (65-bit, 96-bit) rand vars: current-value pin must be bit-exact.
|
||||
w = new;
|
||||
w.w65 = 65'h1_0000_0000_0000_0000;
|
||||
w.w96 = 96'hDEAD_BEEF_CAFE_0000_0000_0001;
|
||||
w.lo65 = 65'h0_FFFF_FFFF_FFFF_FFFF;
|
||||
w.lo96 = 96'h0;
|
||||
i = w.randomize(null);
|
||||
`checkd(i, 1);
|
||||
`checkh(w.w65, 65'h1_0000_0000_0000_0000);
|
||||
`checkh(w.w96, 96'hDEAD_BEEF_CAFE_0000_0000_0001);
|
||||
|
||||
w.w65 = 65'h0_1234_5678_9ABC_DEF0;
|
||||
w.lo65 = 65'h1_FFFF_FFFF_FFFF_FFFF;
|
||||
i = w.randomize(null);
|
||||
`checkd(i, 0);
|
||||
`checkh(w.w65, 65'h0_1234_5678_9ABC_DEF0);
|
||||
|
||||
// 5b. shortint (16-bit, SData) and longint (64-bit, QData) widths --
|
||||
// covers the middle tiers of emitConcreteValue's bit-extraction ladder.
|
||||
wd = new;
|
||||
wd.s16 = 16'sh1234;
|
||||
wd.l64 = 64'sh0123_4567_89AB_CDEF;
|
||||
wd.s_lo = 16'sh0;
|
||||
wd.l_lo = 64'sh0;
|
||||
i = wd.randomize(null);
|
||||
`checkd(i, 1);
|
||||
`checkh(wd.s16, 16'sh1234);
|
||||
`checkh(wd.l64, 64'sh0123_4567_89AB_CDEF);
|
||||
|
||||
wd.s16 = 16'sh0001;
|
||||
wd.s_lo = 16'sh7FFF;
|
||||
i = wd.randomize(null);
|
||||
`checkd(i, 0);
|
||||
`checkh(wd.s16, 16'sh0001);
|
||||
|
||||
// 6. randc: null-call must NOT be poisoned by the exclusion history nor
|
||||
// record values itself; a subsequent real randomize() must still cycle.
|
||||
cyc = new;
|
||||
cyc.lo = 2'd0;
|
||||
repeat (4) begin
|
||||
i = cyc.randomize();
|
||||
`checkd(i, 1);
|
||||
end
|
||||
|
||||
cyc.c = 2'd0; cyc.lo = 2'd0;
|
||||
i = cyc.randomize(null);
|
||||
`checkd(i, 1);
|
||||
`checkd(cyc.c, 2'd0);
|
||||
|
||||
cyc.c = 2'd3; cyc.lo = 2'd0;
|
||||
i = cyc.randomize(null);
|
||||
`checkd(i, 1);
|
||||
`checkd(cyc.c, 2'd3);
|
||||
|
||||
cyc.c = 2'd0; cyc.lo = 2'd1;
|
||||
i = cyc.randomize(null);
|
||||
`checkd(i, 0);
|
||||
`checkd(cyc.c, 2'd0);
|
||||
|
||||
cyc.lo = 2'd0;
|
||||
ok0 = 0; ok1 = 0;
|
||||
repeat (20) begin
|
||||
i = cyc.randomize();
|
||||
`checkd(i, 1);
|
||||
if (cyc.c == 2'd0) ok0 = 1;
|
||||
if (cyc.c == 2'd1) ok1 = 1;
|
||||
end
|
||||
`checkd(ok0, 1);
|
||||
`checkd(ok1, 1);
|
||||
|
||||
// 7. pre_randomize / post_randomize observable behavior.
|
||||
// IEEE 1800-2023 18.6.2: pre is always called.
|
||||
// IEEE 1800-2023 18.6.3: post is called iff randomize() returned 1.
|
||||
cb = new;
|
||||
cb.x = 1; cb.v = 2; cb.pre_count = 0; cb.post_count = 0;
|
||||
i = cb.randomize(null); // sat: pre + post
|
||||
`checkd(i, 1);
|
||||
`checkd(cb.pre_count, 1);
|
||||
`checkd(cb.post_count, 1);
|
||||
|
||||
cb.x = 5; cb.v = 1;
|
||||
i = cb.randomize(null); // unsat: pre only, no post
|
||||
`checkd(i, 0);
|
||||
`checkd(cb.pre_count, 2);
|
||||
`checkd(cb.post_count, 1);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
%Error-UNSUPPORTED: t/t_randomize_null_unsup.v:45:14: Unsupported: 'randomize(null)' on class with rand container or class member
|
||||
: ... note: In instance 't'
|
||||
45 | void'(fq.randomize(null));
|
||||
| ^~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_randomize_null_unsup.v:46:14: Unsupported: 'randomize(null)' on class with rand container or class member
|
||||
: ... note: In instance 't'
|
||||
46 | void'(fu.randomize(null));
|
||||
| ^~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randomize_null_unsup.v:47:14: Unsupported: 'randomize(null)' on class with rand container or class member
|
||||
: ... note: In instance 't'
|
||||
47 | void'(fd.randomize(null));
|
||||
| ^~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randomize_null_unsup.v:48:14: Unsupported: 'randomize(null)' on class with rand container or class member
|
||||
: ... note: In instance 't'
|
||||
48 | void'(fa.randomize(null));
|
||||
| ^~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randomize_null_unsup.v:49:14: Unsupported: 'randomize(null)' on class with rand container or class member
|
||||
: ... note: In instance 't'
|
||||
49 | void'(fw.randomize(null));
|
||||
| ^~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_randomize_null_unsup.v:50:13: Unsupported: 'randomize(null)' on class with rand container or class member
|
||||
: ... note: In instance 't'
|
||||
50 | void'(o.randomize(null));
|
||||
| ^~~~~~~~~
|
||||
%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: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class FooQ;
|
||||
rand int q[$];
|
||||
endclass
|
||||
|
||||
class FooU;
|
||||
rand int u[3];
|
||||
endclass
|
||||
|
||||
class FooD;
|
||||
rand int d[];
|
||||
endclass
|
||||
|
||||
class FooA;
|
||||
rand int a[string];
|
||||
endclass
|
||||
|
||||
class FooW;
|
||||
rand int w[*];
|
||||
endclass
|
||||
|
||||
class Inner;
|
||||
rand int x;
|
||||
constraint c_in {x > 0;}
|
||||
endclass
|
||||
|
||||
class Outer;
|
||||
rand Inner inner;
|
||||
endclass
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
automatic FooQ fq = new;
|
||||
automatic FooU fu = new;
|
||||
automatic FooD fd = new;
|
||||
automatic FooA fa = new;
|
||||
automatic FooW fw = new;
|
||||
automatic Outer o = new;
|
||||
o.inner = new;
|
||||
void'(fq.randomize(null));
|
||||
void'(fu.randomize(null));
|
||||
void'(fd.randomize(null));
|
||||
void'(fa.randomize(null));
|
||||
void'(fw.randomize(null));
|
||||
void'(o.randomize(null));
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue