This commit is contained in:
parent
ee68d1c70c
commit
ed84f3adb2
|
|
@ -1021,8 +1021,8 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
// For global constraints, delete nodep after processing
|
||||
if (isGlobalConstrained && !nodep->backp()) VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
|
||||
// Detect if variable is an array of class references
|
||||
bool isClassRefArray = false;
|
||||
bool isUnpackedClassRefArray = false;
|
||||
AstClassRefDType* elemClassRefDtp = nullptr;
|
||||
{
|
||||
AstNodeDType* varDtp = varp->dtypep()->skipRefp();
|
||||
|
|
@ -1030,12 +1030,14 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
|| VN_IS(varDtp, UnpackArrayDType) || VN_IS(varDtp, AssocArrayDType)) {
|
||||
AstNodeDType* const elemDtp = varDtp->subDTypep()->skipRefp();
|
||||
elemClassRefDtp = VN_CAST(elemDtp, ClassRefDType);
|
||||
if (elemClassRefDtp) isClassRefArray = true;
|
||||
if (elemClassRefDtp) {
|
||||
isClassRefArray = true;
|
||||
isUnpackedClassRefArray = VN_IS(varDtp, UnpackArrayDType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isClassRefArray && !membersel) {
|
||||
// Per-member registration loop for class ref arrays
|
||||
FileLine* const fl = varp->fileline();
|
||||
AstClass* const elemClassp = elemClassRefDtp->classp();
|
||||
AstNodeModule* const varClassp = VN_AS(varp->user2p(), NodeModule);
|
||||
|
|
@ -1049,22 +1051,36 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE},
|
||||
new AstConst{fl, 0}});
|
||||
|
||||
AstVarRef* const arraySizeRef = new AstVarRef{fl, varClassp, varp, VAccess::READ};
|
||||
arraySizeRef->classOrPackagep(classOrPackagep);
|
||||
AstCMethodHard* const sizep
|
||||
= new AstCMethodHard{fl, arraySizeRef, VCMethod::DYN_SIZE, nullptr};
|
||||
sizep->dtypeSetUInt32();
|
||||
AstNodeExpr* sizep;
|
||||
if (isUnpackedClassRefArray) {
|
||||
const int arraySize
|
||||
= VN_AS(varp->dtypep()->skipRefp(), UnpackArrayDType)->elementsConst();
|
||||
sizep = new AstConst{fl, static_cast<uint32_t>(arraySize)};
|
||||
} else {
|
||||
AstVarRef* const arraySizeRef
|
||||
= new AstVarRef{fl, varClassp, varp, VAccess::READ};
|
||||
arraySizeRef->classOrPackagep(classOrPackagep);
|
||||
AstCMethodHard* const dynSizep
|
||||
= new AstCMethodHard{fl, arraySizeRef, VCMethod::DYN_SIZE, nullptr};
|
||||
dynSizep->dtypeSetUInt32();
|
||||
sizep = dynSizep;
|
||||
}
|
||||
|
||||
AstLoop* const loopp = new AstLoop{fl};
|
||||
stmtsp->addNext(loopp);
|
||||
loopp->addStmtsp(new AstLoopTest{
|
||||
fl, loopp, new AstLt{fl, new AstVarRef{fl, iterVarp, VAccess::READ}, sizep}});
|
||||
|
||||
AstVarRef* const arrayAtRef = new AstVarRef{fl, varClassp, varp, VAccess::READ};
|
||||
arrayAtRef->classOrPackagep(classOrPackagep);
|
||||
AstCMethodHard* const atReadp
|
||||
= new AstCMethodHard{fl, arrayAtRef, VCMethod::ARRAY_AT,
|
||||
new AstVarRef{fl, iterVarp, VAccess::READ}};
|
||||
AstVarRef* const arrayReadRef = new AstVarRef{fl, varClassp, varp, VAccess::READ};
|
||||
arrayReadRef->classOrPackagep(classOrPackagep);
|
||||
AstNodeExpr* atReadp;
|
||||
if (isUnpackedClassRefArray) {
|
||||
atReadp = new AstArraySel{fl, arrayReadRef,
|
||||
new AstVarRef{fl, iterVarp, VAccess::READ}};
|
||||
} else {
|
||||
atReadp = new AstCMethodHard{fl, arrayReadRef, VCMethod::ARRAY_AT,
|
||||
new AstVarRef{fl, iterVarp, VAccess::READ}};
|
||||
}
|
||||
atReadp->dtypep(elemClassRefDtp);
|
||||
AstIf* const ifNonNullp = new AstIf{
|
||||
fl, new AstNeq{fl, atReadp, new AstConst{fl, AstConst::Null{}}}, nullptr};
|
||||
|
|
@ -1090,9 +1106,14 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
const int memberWidth = memberDtp->width();
|
||||
|
||||
AstCStmt* const fmtp = new AstCStmt{fl};
|
||||
fmtp->add("VL_SNPRINTF(__Vn, sizeof(__Vn), \"" + smtName + ".%0"
|
||||
+ std::to_string(fmtWidth) + "x." + memberVarp->name()
|
||||
+ "\", (unsigned)");
|
||||
if (isUnpackedClassRefArray) {
|
||||
fmtp->add("VL_SNPRINTF(__Vn, sizeof(__Vn), \"" + smtName + ".%x."
|
||||
+ memberVarp->name() + "\", (unsigned)");
|
||||
} else {
|
||||
fmtp->add("VL_SNPRINTF(__Vn, sizeof(__Vn), \"" + smtName + ".%0"
|
||||
+ std::to_string(fmtWidth) + "x." + memberVarp->name()
|
||||
+ "\", (unsigned)");
|
||||
}
|
||||
fmtp->add(new AstVarRef{fl, iterVarp, VAccess::READ});
|
||||
fmtp->add(");\n");
|
||||
ifNonNullp->addThensp(fmtp);
|
||||
|
|
@ -1100,9 +1121,15 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
AstVarRef* const arrayWrRef
|
||||
= new AstVarRef{fl, varClassp, varp, VAccess::WRITE};
|
||||
arrayWrRef->classOrPackagep(classOrPackagep);
|
||||
AstCMethodHard* const atWritep
|
||||
= new AstCMethodHard{fl, arrayWrRef, VCMethod::ARRAY_AT_WRITE,
|
||||
new AstVarRef{fl, iterVarp, VAccess::READ}};
|
||||
AstNodeExpr* atWritep;
|
||||
if (isUnpackedClassRefArray) {
|
||||
atWritep = new AstArraySel{fl, arrayWrRef,
|
||||
new AstVarRef{fl, iterVarp, VAccess::READ}};
|
||||
} else {
|
||||
atWritep
|
||||
= new AstCMethodHard{fl, arrayWrRef, VCMethod::ARRAY_AT_WRITE,
|
||||
new AstVarRef{fl, iterVarp, VAccess::READ}};
|
||||
}
|
||||
atWritep->dtypep(elemClassRefDtp);
|
||||
AstMemberSel* const memberSelp
|
||||
= new AstMemberSel{fl, atWritep, memberVarp};
|
||||
|
|
@ -1635,10 +1662,46 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
AstNode* rootNode = nodep->fromp();
|
||||
while (const AstMemberSel* const selp = VN_CAST(rootNode, MemberSel))
|
||||
rootNode = selp->fromp();
|
||||
// Detect array/assoc array access in global constraints
|
||||
if (VN_IS(rootNode, ArraySel) || VN_IS(rootNode, AssocSel)) {
|
||||
if (AstArraySel* const arraySelp = VN_CAST(rootNode, ArraySel)) {
|
||||
AstNodeDType* const arrayDtp = arraySelp->fromp()->dtypep()->skipRefp();
|
||||
AstNodeDType* const elemDtp
|
||||
= arrayDtp->subDTypep() ? arrayDtp->subDTypep()->skipRefp() : nullptr;
|
||||
if (elemDtp && VN_IS(elemDtp, ClassRefDType)) {
|
||||
// Nested class ref arrays not yet supported
|
||||
const bool isSimple
|
||||
= nodep->fromp() == rootNode && VN_IS(arraySelp->fromp(), VarRef);
|
||||
if (!isSimple) {
|
||||
nodep->v3warn(
|
||||
E_UNSUPPORTED,
|
||||
"Unsupported: Nested array element access in global constraint");
|
||||
return;
|
||||
}
|
||||
VL_RESTORER(m_structSel);
|
||||
m_structSel = true;
|
||||
nodep->user1(true);
|
||||
arraySelp->user1(true);
|
||||
iterateChildren(nodep);
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstSFormatF* newp = nullptr;
|
||||
if (AstSFormatF* const fromp = VN_CAST(nodep->fromp(), SFormatF)) {
|
||||
if (fromp->name() == "%@.%@") {
|
||||
newp = new AstSFormatF{fl, "%@.%@." + nodep->name(), false,
|
||||
fromp->exprsp()->cloneTreePure(true)};
|
||||
} else {
|
||||
newp = new AstSFormatF{fl, fromp->name() + "." + nodep->name(), false,
|
||||
nullptr};
|
||||
}
|
||||
} else {
|
||||
newp = new AstSFormatF{fl, nodep->name(), false, nullptr};
|
||||
}
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (VN_IS(rootNode, AssocSel) || VN_IS(rootNode, ArraySel)) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Array element access in global constraint ");
|
||||
"Unsupported: Array element access in global constraint");
|
||||
}
|
||||
// Check if the root variable participates in global constraints
|
||||
if (const AstVarRef* const varRefp = VN_CAST(rootNode, VarRef)) {
|
||||
|
|
|
|||
|
|
@ -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,104 @@
|
|||
// 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
|
||||
|
||||
// Test: constraints on class object array element members (fixed-size arrays)
|
||||
|
||||
// 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 check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=[%0d:%0d]\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
/* verilator lint_off WIDTHTRUNC */
|
||||
/* verilator lint_off IMPLICITSTATIC */
|
||||
|
||||
class item_t;
|
||||
rand bit [7:0] value;
|
||||
endclass
|
||||
|
||||
// Scenario A: foreach with range constraint
|
||||
class container_a;
|
||||
rand item_t items[4];
|
||||
constraint val_c {
|
||||
foreach (items[i]) {
|
||||
items[i].value inside {[10:200]};
|
||||
}
|
||||
}
|
||||
function new();
|
||||
foreach (items[i]) items[i] = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Scenario B: foreach with ordering constraint
|
||||
class container_b;
|
||||
rand item_t items[4];
|
||||
constraint order_c {
|
||||
foreach (items[i]) {
|
||||
if (i != 0) {
|
||||
items[i].value > items[i-1].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
function new();
|
||||
foreach (items[i]) items[i] = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Scenario C: constant-index constraint (no foreach)
|
||||
class container_c;
|
||||
rand item_t items[4];
|
||||
constraint val_c {
|
||||
items[0].value < items[1].value;
|
||||
items[1].value < items[2].value;
|
||||
items[2].value < items[3].value;
|
||||
}
|
||||
function new();
|
||||
foreach (items[i]) items[i] = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
automatic container_a ca = new;
|
||||
automatic container_b cb = new;
|
||||
automatic container_c cc = new;
|
||||
automatic int ok;
|
||||
|
||||
repeat (20) begin
|
||||
// Scenario A: foreach range
|
||||
ok = ca.randomize();
|
||||
`checkd(ok, 1);
|
||||
for (int i = 0; i < 4; i++) begin
|
||||
`check_range(ca.items[i].value, 10, 200);
|
||||
end
|
||||
|
||||
// Scenario B: foreach ordering
|
||||
ok = cb.randomize();
|
||||
`checkd(ok, 1);
|
||||
for (int i = 1; i < 4; i++) begin
|
||||
if (cb.items[i].value <= cb.items[i-1].value) begin
|
||||
$write("%%Error: %s:%0d: ordering violated: items[%0d]=%0d <= items[%0d]=%0d\n",
|
||||
`__FILE__, `__LINE__, i, cb.items[i].value, i-1, cb.items[i-1].value);
|
||||
`stop;
|
||||
end
|
||||
end
|
||||
|
||||
// Scenario C: constant-index ordering
|
||||
ok = cc.randomize();
|
||||
`checkd(ok, 1);
|
||||
for (int i = 1; i < 4; i++) begin
|
||||
if (cc.items[i].value <= cc.items[i-1].value) begin
|
||||
$write("%%Error: %s:%0d: ordering violated: items[%0d]=%0d <= items[%0d]=%0d\n",
|
||||
`__FILE__, `__LINE__, i, cc.items[i].value, i-1, cc.items[i-1].value);
|
||||
`stop;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,26 +1,20 @@
|
|||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:41:20: Unsupported: Array element access in global constraint
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:41:20: Unsupported: Nested array element access in global constraint
|
||||
41 | m_mid.m_arr[0].m_x == 200;
|
||||
| ^~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:42:20: Unsupported: Array element access in global constraint
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:42:20: Unsupported: Nested array element access in global constraint
|
||||
42 | m_mid.m_arr[0].m_y == 201;
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:47:24: Unsupported: Array element access in global constraint
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:47:24: Unsupported: Nested array element access in global constraint
|
||||
47 | m_mid_arr[0].m_obj.m_x == 300;
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:47:18: Unsupported: Array element access in global constraint
|
||||
47 | m_mid_arr[0].m_obj.m_x == 300;
|
||||
| ^~~~~
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:48:24: Unsupported: Array element access in global constraint
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:48:24: Unsupported: Nested array element access in global constraint
|
||||
48 | m_mid_arr[0].m_obj.m_y == 301;
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:48:18: Unsupported: Array element access in global constraint
|
||||
48 | m_mid_arr[0].m_obj.m_y == 301;
|
||||
| ^~~~~
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:53:27: Unsupported: Array element access in global constraint
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:53:27: Unsupported: Nested array element access in global constraint
|
||||
53 | m_mid_arr[1].m_arr[2].m_y == 400;
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:53:18: Unsupported: Array element access in global constraint
|
||||
53 | m_mid_arr[1].m_arr[2].m_y == 400;
|
||||
| ^~~~~
|
||||
%Error-UNSUPPORTED: t/t_constraint_global_arr_unsup.v:59:16: Unsupported: Array element access in global constraint
|
||||
59 | m_assoc[0].m_x == 500;
|
||||
| ^~~
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@ class Outer;
|
|||
constraint c_multi_array {
|
||||
m_mid_arr[1].m_arr[2].m_y == 400;
|
||||
}
|
||||
|
||||
// Case 5: Associative array element member access
|
||||
rand Inner m_assoc[int];
|
||||
constraint c_assoc {
|
||||
m_assoc[0].m_x == 500;
|
||||
}
|
||||
endclass
|
||||
|
||||
module t_constraint_global_arr_unsup;
|
||||
|
|
|
|||
Loading…
Reference in New Issue