This commit is contained in:
Yilou Wang 2025-10-29 14:40:24 +01:00
parent 04f7251975
commit 12747ae18f
4 changed files with 138 additions and 141 deletions

View File

@ -307,7 +307,6 @@ class RandomizeMarkVisitor final : public VNVisitor {
// Process per-variable (object instance), not per-class // Process per-variable (object instance), not per-class
// This allows multiple objects of the same class (e.g., obj1 and obj2 of type Sub) // This allows multiple objects of the same class (e.g., obj1 and obj2 of type Sub)
if (m_processedVars.insert(objVar).second) { if (m_processedVars.insert(objVar).second) {
// Clone constraints from the top-level class (e.g., Level1 for obj_a) // Clone constraints from the top-level class (e.g., Level1 for obj_a)
gConsClass->foreachMember([&](AstClass* const classp, AstConstraint* const constrp) { gConsClass->foreachMember([&](AstClass* const classp, AstConstraint* const constrp) {
AstConstraint* const cloneConstrp = constrp->cloneTree(false); AstConstraint* const cloneConstrp = constrp->cloneTree(false);
@ -611,7 +610,8 @@ class RandomizeMarkVisitor final : public VNVisitor {
if (VN_IS(nodep->fromp(), VarRef) || VN_IS(nodep->fromp(), MemberSel)) { if (VN_IS(nodep->fromp(), VarRef) || VN_IS(nodep->fromp(), MemberSel)) {
markNestedGlobalConstrainedRecurse(nodep->fromp()); markNestedGlobalConstrainedRecurse(nodep->fromp());
} else if (VN_IS(nodep->fromp(), ArraySel)) { } else if (VN_IS(nodep->fromp(), ArraySel)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported in global constraint"); nodep->v3warn(E_UNSUPPORTED, "Unsupported: " << nodep->prettyTypeName()
<< " within a global constraint");
} }
// Global constraint processing algorithm: // Global constraint processing algorithm:
// 1. Detect globally constrained object variables in randomized classes // 1. Detect globally constrained object variables in randomized classes
@ -832,7 +832,6 @@ class ConstraintExprVisitor final : public VNVisitor {
AstMemberSel* membersel = nullptr; AstMemberSel* membersel = nullptr;
std::string smtName; std::string smtName;
if (isGlobalConstrained && VN_IS(nodep->backp(), MemberSel)) { if (isGlobalConstrained && VN_IS(nodep->backp(), MemberSel)) {
// For global constraints: build complete path from topmost MemberSel // For global constraints: build complete path from topmost MemberSel
AstNode* topMemberSel = nodep->backp(); AstNode* topMemberSel = nodep->backp();
@ -1119,11 +1118,9 @@ class ConstraintExprVisitor final : public VNVisitor {
} }
void visit(AstMemberSel* nodep) override { void visit(AstMemberSel* nodep) override {
if (nodep->varp()->rand().isRandomizable() && nodep->fromp()) { if (nodep->varp()->rand().isRandomizable() && nodep->fromp()) {
AstNode* rootNode = nodep->fromp(); AstNode* rootNode = nodep->fromp();
while (const AstMemberSel* const selp = VN_CAST(rootNode, MemberSel)) while (const AstMemberSel* const selp = VN_CAST(rootNode, MemberSel))
rootNode = selp->fromp(); rootNode = selp->fromp();
// Check if the root variable participates in global constraints // Check if the root variable participates in global constraints
if (const AstVarRef* const varRefp = VN_CAST(rootNode, VarRef)) { if (const AstVarRef* const varRefp = VN_CAST(rootNode, VarRef)) {
AstVar* const constrainedVar = varRefp->varp(); AstVar* const constrainedVar = varRefp->varp();

View File

@ -1,26 +1,26 @@
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:41:20: Unsupported in global constraint %Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:41:20: Unsupported: MEMBERSEL 'm_x' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup' : ... note: In instance 't_constraint_global_arr_unsup'
41 | mid.arr[0].x == 200; 41 | m_mid.m_arr[0].m_x == 200;
| ^ | ^~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:42:20: Unsupported in global constraint %Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:42:20: Unsupported: MEMBERSEL 'm_y' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup' : ... note: In instance 't_constraint_global_arr_unsup'
42 | mid.arr[0].y == 201; 42 | m_mid.m_arr[0].m_y == 201;
| ^
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:47:20: Unsupported in global constraint
: ... note: In instance 't_constraint_global_arr_unsup'
47 | mid_arr[0].obj.x == 300;
| ^~~ | ^~~
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:48:20: Unsupported in global constraint %Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:47:18: Unsupported: MEMBERSEL 'm_obj' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup' : ... note: In instance 't_constraint_global_arr_unsup'
48 | mid_arr[0].obj.y == 301; 47 | m_mid_arr[0].m_obj.m_x == 300;
| ^~~ | ^~~~~
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:53:20: Unsupported in global constraint %Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:48:18: Unsupported: MEMBERSEL 'm_obj' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup' : ... note: In instance 't_constraint_global_arr_unsup'
53 | mid_arr[1].arr[2].y == 400; 48 | m_mid_arr[0].m_obj.m_y == 301;
| ^~~ | ^~~~~
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:53:27: Unsupported in global constraint %Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:53:18: Unsupported: MEMBERSEL 'm_arr' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup' : ... note: In instance 't_constraint_global_arr_unsup'
53 | mid_arr[1].arr[2].y == 400; 53 | m_mid_arr[1].m_arr[2].m_y == 400;
| ^ | ^~~~~
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:53:27: Unsupported: MEMBERSEL 'm_y' within a global constraint
: ... note: In instance 't_constraint_global_arr_unsup'
53 | m_mid_arr[1].m_arr[2].m_y == 400;
| ^~~
%Error: Exiting due to %Error: Exiting due to

View File

@ -6,81 +6,81 @@
/* verilator lint_off WIDTHTRUNC */ /* verilator lint_off WIDTHTRUNC */
class Inner; class Inner;
rand int x; rand int m_x;
rand int y; rand int m_y;
endclass endclass
class Middle; class Middle;
rand Inner obj; rand Inner m_obj;
rand Inner arr[3]; rand Inner m_arr[3];
endclass endclass
class Outer; class Outer;
rand Middle mid; rand Middle m_mid;
rand Middle mid_arr[2]; rand Middle m_mid_arr[2];
function new(); function new();
mid = new; m_mid = new;
mid.obj = new; m_mid.m_obj = new;
foreach (mid.arr[i]) mid.arr[i] = new; foreach (m_mid.m_arr[i]) m_mid.m_arr[i] = new;
foreach (mid_arr[i]) begin foreach (m_mid_arr[i]) begin
mid_arr[i] = new; m_mid_arr[i] = new;
mid_arr[i].obj = new; m_mid_arr[i].m_obj = new;
foreach (mid_arr[i].arr[j]) mid_arr[i].arr[j] = new; foreach (m_mid_arr[i].m_arr[j]) m_mid_arr[i].m_arr[j] = new;
end end
endfunction endfunction
// Case 1: Simple nested member access (should work) // Case 1: Simple nested member access (should work)
constraint c_simple { constraint c_simple {
mid.obj.x == 100; m_mid.m_obj.m_x == 100;
mid.obj.y == 101; m_mid.m_obj.m_y == 101;
} }
// Case 2: Array indexing in the path (may not work) // Case 2: Array indexing in the path (may not work)
constraint c_array_index { constraint c_array_index {
mid.arr[0].x == 200; m_mid.m_arr[0].m_x == 200;
mid.arr[0].y == 201; m_mid.m_arr[0].m_y == 201;
} }
// Case 3: Nested array indexing // Case 3: Nested array indexing
constraint c_nested_array { constraint c_nested_array {
mid_arr[0].obj.x == 300; m_mid_arr[0].m_obj.m_x == 300;
mid_arr[0].obj.y == 301; m_mid_arr[0].m_obj.m_y == 301;
} }
// Case 4: Multiple array indices // Case 4: Multiple array indices
constraint c_multi_array { constraint c_multi_array {
mid_arr[1].arr[2].y == 400; m_mid_arr[1].m_arr[2].m_y == 400;
} }
endclass endclass
module t_constraint_global_arr_unsup; module t_constraint_global_arr_unsup;
initial begin initial begin
Outer o = new; Outer o = new;
if (o.randomize()) begin if (o.randomize()) begin
$display("Case 1 - Simple: mid.obj.x = %0d (expected 100)", o.mid.obj.x); $display("Case 1 - Simple: mid.obj.x = %0d (expected 100)", o.m_mid.m_obj.m_x);
$display("Case 1 - Simple: mid.obj.y = %0d (expected 101)", o.mid.obj.y); $display("Case 1 - Simple: mid.obj.y = %0d (expected 101)", o.m_mid.m_obj.m_y);
$display("Case 2 - Array[0]: mid.arr[0].x = %0d (expected 200)", o.mid.arr[0].x); $display("Case 2 - Array[0]: mid.arr[0].x = %0d (expected 200)", o.m_mid.m_arr[0].m_x);
$display("Case 2 - Array[0]: mid.arr[0].y = %0d (expected 201)", o.mid.arr[0].y); $display("Case 2 - Array[0]: mid.arr[0].y = %0d (expected 201)", o.m_mid.m_arr[0].m_y);
$display("Case 3 - Nested[0]: mid_arr[0].obj.x = %0d (expected 300)", o.mid_arr[0].obj.x); $display("Case 3 - Nested[0]: mid_arr[0].obj.x = %0d (expected 300)", o.m_mid_arr[0].m_obj.m_x);
$display("Case 3 - Nested[0]: mid_arr[0].obj.y = %0d (expected 301)", o.mid_arr[0].obj.y); $display("Case 3 - Nested[0]: mid_arr[0].obj.y = %0d (expected 301)", o.m_mid_arr[0].m_obj.m_y);
$display("Case 4 - Multi[1][2]: mid_arr[1].arr[2].y = %0d (expected 400)", o.mid_arr[1].arr[2].y); $display("Case 4 - Multi[1][2]: mid_arr[1].arr[2].y = %0d (expected 400)", o.m_mid_arr[1].m_arr[2].m_y);
// Check results // Check results
if (o.mid.obj.x == 100 && o.mid.obj.y == 101 && if (o.m_mid.m_obj.m_x == 100 && o.m_mid.m_obj.m_y == 101 &&
o.mid.arr[0].x == 200 && o.mid.arr[0].y == 201 && o.m_mid.m_arr[0].m_x == 200 && o.m_mid.m_arr[0].m_y == 201 &&
o.mid_arr[0].obj.x == 300 && o.mid_arr[0].obj.y == 301 && o.m_mid_arr[0].m_obj.m_x == 300 && o.m_mid_arr[0].m_obj.m_y == 301 &&
o.mid_arr[1].arr[2].y == 400) begin o.m_mid_arr[1].m_arr[2].m_y == 400) begin
$display("*-* All Finished *-*"); $display("*-* All Finished *-*");
$finish; $finish;
end else begin end else begin
$display("*-* FAILED *-*"); $display("*-* FAILED *-*");
$stop; $stop;
end end
end else begin end else begin
$display("*-* FAILED: randomize() returned 0 *-*"); $display("*-* FAILED: randomize() returned 0 *-*");
$stop; $stop;
end
end end
end
endmodule endmodule
/* verilator lint_off WIDTHTRUNC */ /* verilator lint_off WIDTHTRUNC */

View File

@ -5,81 +5,81 @@
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
class Inner; class Inner;
rand int val; rand int m_val;
constraint c_local { val inside {[1:5]}; } constraint c_local { m_val inside {[1:5]}; }
function new(); val = 0; endfunction function new(); m_val = 0; endfunction
endclass endclass
class Mid; class Mid;
int limit; int m_limit;
rand int x; rand int m_x;
rand Inner inner; rand Inner m_inner;
constraint c_mid { x == limit; } constraint c_mid { m_x == m_limit; }
function new(int lim); function new(int lim);
limit = lim; m_limit = lim;
inner = new(); m_inner = new();
endfunction endfunction
endclass endclass
class Top; class Top;
rand Mid m1; rand Mid m_m1;
rand Mid m2; rand Mid m_m2;
rand int y; rand int m_y;
constraint c_global { constraint c_global {
m1.inner.val < m2.inner.val; m_m1.m_inner.m_val < m_m2.m_inner.m_val;
y > m1.x; m_y > m_m1.m_x;
y < m2.x; m_y < m_m2.m_x;
m1.inner.val + m2.inner.val < 8; m_m1.m_inner.m_val + m_m2.m_inner.m_val < 8;
} }
function new(); function new();
m1 = new(3); m_m1 = new(3);
m2 = new(5); m_m2 = new(5);
y = 0; m_y = 0;
endfunction endfunction
endclass endclass
module t_constraint_global_random; module t_constraint_global_random;
int success; int success;
Top t; Top t;
initial begin initial begin
t = new(); t = new();
// Test 1: Regular randomize() with global constraints // Test 1: Regular randomize() with global constraints
success = t.randomize(); success = t.randomize();
if (success != 1) $stop; if (success != 1) $stop;
// $display("m1.x=%0d, m2.x=%0d, y=%0d", t.m1.x, t.m2.x, t.y); // $display("m1.x=%0d, m2.x=%0d, y=%0d", t.m_m1.m_x, t.m_m2.m_x, t.m_y);
// $display("m1.inner.val=%0d, m2.inner.val=%0d", t.m1.inner.val, t.m2.inner.val); // $display("m1.inner.val=%0d, m2.inner.val=%0d", t.m_m1.m_inner.m_val, t.m_m2.m_inner.m_val);
if (t.m1.x != 3 || t.m2.x != 5) $stop; if (t.m_m1.m_x != 3 || t.m_m2.m_x != 5) $stop;
if (t.m1.inner.val >= t.m2.inner.val) $stop; if (t.m_m1.m_inner.m_val >= t.m_m2.m_inner.m_val) $stop;
if (t.y <= t.m1.x || t.y >= t.m2.x) $stop; if (t.m_y <= t.m_m1.m_x || t.m_y >= t.m_m2.m_x) $stop;
if (t.m1.inner.val + t.m2.inner.val >= 8) $stop; if (t.m_m1.m_inner.m_val + t.m_m2.m_inner.m_val >= 8) $stop;
if (t.m1.inner.val < 1 || t.m1.inner.val > 5 || if (t.m_m1.m_inner.m_val < 1 || t.m_m1.m_inner.m_val > 5 ||
t.m2.inner.val < 1 || t.m2.inner.val > 5) $stop; t.m_m2.m_inner.m_val < 1 || t.m_m2.m_inner.m_val > 5) $stop;
// Test 2: randomize() with inline constraint on global-constrained members // Test 2: randomize() with inline constraint on global-constrained members
success = 0; success = 0;
success = t.randomize() with { success = t.randomize() with {
m1.inner.val == 2; m_m1.m_inner.m_val == 2;
m2.inner.val == 5; m_m2.m_inner.m_val == 5;
}; };
if (success != 1) $stop; if (success != 1) $stop;
// Verify inline constraints // Verify inline constraints
if (t.m1.inner.val != 2) $stop; if (t.m_m1.m_inner.m_val != 2) $stop;
if (t.m2.inner.val != 5) $stop; if (t.m_m2.m_inner.m_val != 5) $stop;
// Verify global constraints still hold // Verify global constraints still hold
if (t.m1.x != 3 || t.m2.x != 5) $stop; if (t.m_m1.m_x != 3 || t.m_m2.m_x != 5) $stop;
if (t.m1.inner.val >= t.m2.inner.val) $stop; if (t.m_m1.m_inner.m_val >= t.m_m2.m_inner.m_val) $stop;
if (t.y <= t.m1.x || t.y >= t.m2.x) $stop; if (t.m_y <= t.m_m1.m_x || t.m_y >= t.m_m2.m_x) $stop;
if (t.m1.inner.val + t.m2.inner.val >= 8) $stop; if (t.m_m1.m_inner.m_val + t.m_m2.m_inner.m_val >= 8) $stop;
$write("*-* All Finished *-*\n"); $write("*-* All Finished *-*\n");
$finish; $finish;
end end
endmodule endmodule