add tests
This commit is contained in:
parent
b69e0569cf
commit
591d74c5d8
|
|
@ -60,6 +60,12 @@ enum ClassRandom : uint8_t {
|
|||
IS_RANDOMIZED_INLINE_WITH_GLOBAL_CONSTRAINTS, // randomize() with args and global constraints
|
||||
};
|
||||
|
||||
// ######################################################################
|
||||
// Constants for global constraint processing
|
||||
|
||||
static constexpr const char* GLOBAL_CONSTRAINT_SEPARATOR = "__DT__";
|
||||
static constexpr const char* BASIC_RANDOMIZE_FUNC_NAME = "__Vbasic_randomize";
|
||||
|
||||
// ######################################################################
|
||||
// Establishes the target of a rand_mode() call
|
||||
|
||||
|
|
@ -145,10 +151,6 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
std::vector<AstConstraint*> m_clonedConstraints;
|
||||
std::unordered_set<const AstVar*> m_processedVars; // Track by variable instance, not class
|
||||
|
||||
// Constants for global constraint processing
|
||||
static constexpr const char* GLOBAL_CONSTRAINT_SEPARATOR = "__DT__";
|
||||
static constexpr const char* BASIC_RANDOMIZE_FUNC_NAME = "__Vbasic_randomize";
|
||||
|
||||
// METHODS
|
||||
void markMembers(const AstClass* nodep) {
|
||||
for (const AstClass* classp = nodep; classp;
|
||||
|
|
@ -216,7 +218,7 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
} else if (VN_IS(nodep, MemberSel)) {
|
||||
AstMemberSel* memberSelp = VN_AS(nodep, MemberSel);
|
||||
memberSelp->varp()->setGlobalConstrained(true);
|
||||
markNestedGlobalConstrained(memberSelp->fromp()); // Recurse up the chain
|
||||
markNestedGlobalConstrained(memberSelp->fromp());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,18 +228,16 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
for (AstVar* memberVarp : path) {
|
||||
AstMemberSel* memberSel
|
||||
= new AstMemberSel(rootVarRefp->fileline(), current, memberVarp);
|
||||
memberSel->user2p(m_classp); // Set containing class for all MemberSel nodes
|
||||
memberSel->user2p(m_classp);
|
||||
current = memberSel;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// Clone constraints from nested rand class members recursively
|
||||
// Clone constraints from nested rand class members
|
||||
void cloneNestedConstraintsRecursive(AstVarRef* rootVarRefp, AstClass* classp,
|
||||
const std::vector<AstVar*>& pathToClass) {
|
||||
if (!classp) return;
|
||||
|
||||
// Recursively process nested rand members first
|
||||
for (AstNode* memberNodep = classp->membersp(); memberNodep;
|
||||
memberNodep = memberNodep->nextp()) {
|
||||
if (AstVar* memberVarp = VN_CAST(memberNodep, Var)) {
|
||||
|
|
@ -268,11 +268,10 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
cloneConstrp->foreach([&](AstVarRef* varRefp) {
|
||||
if (!varRefp || !varRefp->varp()) return;
|
||||
|
||||
// Build chain: rootVarRef.pathMember1.pathMember2...finalVar
|
||||
AstNodeExpr* chain = buildMemberSelChain(rootVarRefp, newPath);
|
||||
AstMemberSel* finalSel = new AstMemberSel(varRefp->fileline(),
|
||||
chain, varRefp->varp());
|
||||
finalSel->user2p(m_classp); // Set containing class
|
||||
finalSel->user2p(m_classp);
|
||||
varRefp->replaceWith(finalSel);
|
||||
VL_DO_DANGLING(varRefp->deleteTree(), varRefp);
|
||||
});
|
||||
|
|
@ -280,7 +279,6 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
m_clonedConstraints.push_back(cloneConstrp);
|
||||
});
|
||||
|
||||
// Recurse deeper
|
||||
cloneNestedConstraintsRecursive(rootVarRefp, nestedClassp, newPath);
|
||||
}
|
||||
}
|
||||
|
|
@ -288,29 +286,19 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
// Wrapper to start the recursive cloning
|
||||
void cloneNestedConstraints(AstVarRef* rootVarRefp, AstClass* rootClass) {
|
||||
std::vector<AstVar*> emptyPath;
|
||||
cloneNestedConstraintsRecursive(rootVarRefp, rootClass, emptyPath);
|
||||
}
|
||||
|
||||
void nameManipulation(AstVarRef* fromp, AstConstraint* cloneCons) {
|
||||
if (!fromp || !cloneCons) {
|
||||
UINFO(9, "Invalid parameters in nameManipulation\n");
|
||||
return;
|
||||
}
|
||||
if (!fromp || !cloneCons) return;
|
||||
// Iterate through all variable references and replace them with member selections.
|
||||
cloneCons->name(fromp->name() + GLOBAL_CONSTRAINT_SEPARATOR + cloneCons->name());
|
||||
cloneCons->foreach([&](AstVarRef* varRefp) {
|
||||
if (!varRefp || !varRefp->varp()) {
|
||||
UINFO(9, "Invalid variable reference in constraint\n");
|
||||
return;
|
||||
}
|
||||
if (!varRefp || !varRefp->varp()) return;
|
||||
AstVarRef* clonedFromp = fromp->cloneTree(false);
|
||||
if (!clonedFromp) {
|
||||
UINFO(9, "Failed to clone variable reference in nameManipulation\n");
|
||||
return;
|
||||
}
|
||||
if (!clonedFromp) return;
|
||||
AstMemberSel* varMemberp
|
||||
= new AstMemberSel{cloneCons->fileline(), clonedFromp, varRefp->varp()};
|
||||
varMemberp->user2p(m_classp);
|
||||
|
|
@ -642,15 +630,9 @@ class RandomizeMarkVisitor final : public VNVisitor {
|
|||
// Clone constraints from the top-level class (e.g., Level1 for obj_a)
|
||||
gConsClass->foreachMember([&](AstClass* const classp,
|
||||
AstConstraint* const constrp) {
|
||||
if (!constrp) {
|
||||
UINFO(9, "Null constraint found in class " << classp->name() << "\n");
|
||||
return;
|
||||
}
|
||||
if (!constrp) return;
|
||||
AstConstraint* cloneConstrp = constrp->cloneTree(false);
|
||||
if (!cloneConstrp) {
|
||||
UINFO(9, "Failed to clone constraint " << constrp->name() << "\n");
|
||||
return;
|
||||
}
|
||||
if (!cloneConstrp) return;
|
||||
// Name manipulation
|
||||
nameManipulation(varRefp, cloneConstrp);
|
||||
m_clonedConstraints.push_back(cloneConstrp);
|
||||
|
|
@ -1630,9 +1612,6 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
m_clonedConstraints; // Map of the class to the cloned constraint from the instantiated
|
||||
// object
|
||||
|
||||
// Constants for global constraint processing
|
||||
static constexpr const char* BASIC_RANDOMIZE_FUNC_NAME = "__Vbasic_randomize";
|
||||
|
||||
// METHODS
|
||||
void createRandomGenerator(AstClass* const classp) {
|
||||
if (classp->user3p()) return;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
#!/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')
|
||||
|
||||
if not test.have_solver:
|
||||
test.skip("No constraint solver installed")
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// 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 Inner;
|
||||
rand int val;
|
||||
constraint c_local { val inside {[1:5]}; }
|
||||
function new(); val = 0; endfunction
|
||||
endclass
|
||||
|
||||
class Mid;
|
||||
int limit;
|
||||
rand int x;
|
||||
rand Inner inner;
|
||||
constraint c_mid { x == limit; }
|
||||
function new(int lim);
|
||||
limit = lim;
|
||||
inner = new();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Top;
|
||||
rand Mid m1;
|
||||
rand Mid m2;
|
||||
rand int y;
|
||||
|
||||
constraint c_global {
|
||||
m1.inner.val < m2.inner.val;
|
||||
y > m1.x;
|
||||
y < m2.x;
|
||||
m1.inner.val + m2.inner.val < 8;
|
||||
}
|
||||
|
||||
function new();
|
||||
m1 = new(3);
|
||||
m2 = new(5);
|
||||
y = 0;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t_constraint_global_random;
|
||||
int success;
|
||||
Top t;
|
||||
|
||||
initial begin
|
||||
t = new();
|
||||
success = t.randomize();
|
||||
if (success != 1) $stop;
|
||||
|
||||
// $display("m1.x=%0d, m2.x=%0d, y=%0d", t.m1.x, t.m2.x, t.y);
|
||||
// $display("m1.inner.val=%0d, m2.inner.val=%0d", t.m1.inner.val, t.m2.inner.val);
|
||||
|
||||
if (t.m1.x != 3 || t.m2.x != 5) $stop;
|
||||
if (t.m1.inner.val >= t.m2.inner.val) $stop;
|
||||
if (t.y <= t.m1.x || t.y >= t.m2.x) $stop;
|
||||
if (t.m1.inner.val + t.m2.inner.val >= 8) $stop;
|
||||
if (t.m1.inner.val < 1 || t.m1.inner.val > 5 ||
|
||||
t.m2.inner.val < 1 || t.m2.inner.val > 5) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue