Support constraints on associative array user-defined keys (#5671) (#5729)

This commit is contained in:
Yilou Wang 2025-01-11 18:07:52 +01:00 committed by GitHub
parent dfe28f7ed0
commit d3e205f201
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 253 additions and 25 deletions

View File

@ -505,6 +505,12 @@ public:
VlQueue& operator=(VlQueue&&) = default;
bool operator==(const VlQueue& rhs) const { return m_deque == rhs.m_deque; }
bool operator!=(const VlQueue& rhs) const { return m_deque != rhs.m_deque; }
bool operator<(const VlQueue& rhs) const {
for (int index = 0; index < m_deque.size(); ++index) {
if (m_deque[index] < rhs.m_deque[index]) return true;
}
return false;
}
// Standard copy constructor works. Verilog: assoca = assocb
// Also must allow conversion from a different N_MaxSize queue
@ -1364,6 +1370,12 @@ public:
bool neq(const T_Value that[N_Depth]) const { return neq(*this, that); }
void assign(const T_Value that[N_Depth]) { std::copy_n(that, N_Depth, m_storage); }
void operator=(const T_Value that[N_Depth]) { assign(that); }
bool operator<(const VlUnpacked<T_Value, N_Depth>& that) const {
for (int index = 0; index < N_Depth; ++index) {
if (m_storage[index] < that.m_storage[index]) return true;
}
return false;
}
// inside (set membership operator)
bool inside(const T_Value& value) const {

View File

@ -262,6 +262,23 @@ class EmitCHeader final : public EmitCConstInit {
putns(sdtypep, "bool operator!=(const " + EmitCBase::prefixNameProtect(sdtypep)
+ "& rhs) const {\n");
puts("return !(*this == rhs);\n}\n");
putns(sdtypep, "\nbool operator<(const " + EmitCBase::prefixNameProtect(sdtypep)
+ "& rhs) const {\n");
puts("return ");
puts("std::tie(");
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (itemp != sdtypep->membersp()) puts(", ");
putns(itemp, itemp->nameProtect());
}
puts(")\n < std::tie(");
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
if (itemp != sdtypep->membersp()) puts(", ");
putns(itemp, "rhs." + itemp->nameProtect());
}
puts(");\n");
puts("}\n");
puts("};\n");
}

View File

@ -709,7 +709,7 @@ class ConstraintExprVisitor final : public VNVisitor {
void visit(AstAssocSel* nodep) override {
if (editFormat(nodep)) return;
FileLine* const fl = nodep->fileline();
if (VN_IS(nodep->bitp(), CvtPackString)) {
if (VN_IS(nodep->bitp(), CvtPackString) && VN_IS(nodep->bitp()->dtypep(), BasicDType)) {
AstCvtPackString* const stringp = VN_AS(nodep->bitp(), CvtPackString);
const size_t stringSize = VN_AS(stringp->lhsp(), Const)->width();
if (stringSize > 128) {
@ -724,22 +724,32 @@ class ConstraintExprVisitor final : public VNVisitor {
handle.relink(idxp);
editSMT(nodep, nodep->fromp(), idxp);
} else {
VNRelinker handle;
const int actual_width = nodep->bitp()->width();
std::string fmt;
// Normalize to standard bit width
if (actual_width <= 8) {
fmt = "#x%2x";
} else if (actual_width <= 16) {
fmt = "#x%4x";
} else {
fmt = "#x%" + std::to_string(VL_WORDS_I(actual_width) * 8) + "x";
}
if (VN_IS(nodep->bitp()->dtypep(), BasicDType)
|| (VN_IS(nodep->bitp()->dtypep(), StructDType)
&& VN_AS(nodep->bitp()->dtypep(), StructDType)->packed())
|| VN_IS(nodep->bitp()->dtypep(), EnumDType)
|| VN_IS(nodep->bitp()->dtypep(), PackArrayDType)) {
VNRelinker handle;
const int actual_width = nodep->bitp()->width();
std::string fmt;
// Normalize to standard bit width
if (actual_width <= 8) {
fmt = "#x%2x";
} else if (actual_width <= 16) {
fmt = "#x%4x";
} else {
fmt = "#x%" + std::to_string(VL_WORDS_I(actual_width) * 8) + "x";
}
AstNodeExpr* const idxp
= new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)};
handle.relink(idxp);
editSMT(nodep, nodep->fromp(), idxp);
AstNodeExpr* const idxp
= new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)};
handle.relink(idxp);
editSMT(nodep, nodep->fromp(), idxp);
} else {
nodep->bitp()->v3error(
"Illegal non-integral expression or subexpression in random constraint."
" (IEEE 1800-2023 18.3)");
}
}
}
void visit(AstArraySel* nodep) override {

View File

@ -3,4 +3,13 @@
| ^~~~~~~~~~~~~~~~~~~~
... 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: t/t_constraint_assoc_arr_bad.v:30:26: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3)
30 | constraint c1 { data[cl] > 0;}
| ^~
%Error: t/t_constraint_assoc_arr_bad.v:44:44: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3)
44 | constraint c2 { foreach (data[i]) data[i] < 100; }
| ^
%Error: t/t_constraint_assoc_arr_bad.v:58:44: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3)
58 | constraint c3 { foreach (data[i]) data[i] > 0; }
| ^
%Error: Exiting due to

View File

@ -4,8 +4,8 @@
// any use, without warranty, 2024 by PlanV GmbH.
// SPDX-License-Identifier: CC0-1.0
class AssocArrayWarningTest;
// Long String index associative array
class AssocArrayString;
rand int string_arr [string];
constraint c {
@ -14,20 +14,79 @@ class AssocArrayWarningTest;
function new();
string_arr["a_very_long_string"] = 0;
endfunction
endclass
class keyClass;
int id;
function new();
id = 3;
endfunction
endclass
// Class index associative array
class AssocArrayClass;
rand bit [31:0] data [keyClass];
keyClass cl;
// constraint c4 { foreach (data[i]) data[i] > 0;} Unsupported index type for an associative array in an iterative constraint.
constraint c1 { data[cl] > 0;} // Illegal index expression of unpacked type in constraint.
function new();
cl = new();
data[cl] = 32'd77;
endfunction
endclass
typedef struct {
int a;
int b;
} UnpackedIndexType;
// Struct (unpacked) index associative array
class AssocArrayUnpackedStruct;
rand bit [31:0] data [UnpackedIndexType];
constraint c2 { foreach (data[i]) data[i] < 100; } // Illegal non-integral expression in random constraint.
function new();
UnpackedIndexType idx;
idx.a = 1;
idx.b = 2;
data[idx] = 32'd25;
endfunction
endclass
// Array (unpacked) index associative array
typedef logic [2:0] IndexArrayType[3];
class AssocArrayArrayIndex;
rand bit [31:0] data [IndexArrayType];
constraint c3 { foreach (data[i]) data[i] > 0; }
function new();
IndexArrayType idx;
for (int j = 0; j < 4; j++) begin
idx[j] = 3'd0;
end
data[idx] = 32'd75;
endfunction
endclass
module t_constraint_assoc_arr_bad;
AssocArrayWarningTest test_obj;
AssocArrayString test_str;
AssocArrayClass test_cls;
AssocArrayUnpackedStruct test_unp_struct;
AssocArrayArrayIndex test_unp_arr;
int success = 0;
initial begin
test_obj = new();
repeat(2) begin
int success;
success = test_obj.randomize();
if (success != 1) $stop;
end
test_str = new();
test_cls = new();
test_unp_struct = new();
test_unp_arr = new();
success += test_str.randomize();
success += test_cls.randomize();
success += test_unp_struct.randomize();
success += test_unp_arr.randomize();
if(success != 4) $stop;
$write("*-* All Finished *-*\n");
$finish;
end

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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()

View File

@ -0,0 +1,100 @@
// 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
// Enum-based associative array
typedef enum { RED, GREEN, YELLOW } color_t;
class AssocArrayEnum;
rand bit [7:0] colors [color_t];
constraint c1 { foreach (colors[i]) colors[i] == 4; }
function new();
colors[RED] = 8'd5;
colors[GREEN] = 8'd10;
colors[YELLOW] = 8'd15;
endfunction
function void self_check();
foreach (colors[i]) begin
if (colors[i] != 4) $stop;
end
endfunction
endclass
// Struct (packed) index associative array
typedef struct packed {
bit [2:0] high;
bit [1:0] low;
} PackedIndexType;
class AssocArrayPackedStruct;
rand bit [31:0] data [PackedIndexType];
constraint c2 { foreach (data[i]) data[i] == 100; }
function new();
PackedIndexType idx;
idx.high = 3'd1;
idx.low = 2'd1;
data[idx] = 32'd50;
endfunction
function void self_check();
foreach (data[i]) begin
if (data[i] != 100) $stop;
end
endfunction
endclass
// Array (packed) index associative array
typedef logic [2:0][7:0] IndexArrayType;
class AssocArrayArrayIndex;
rand bit [31:0] data [IndexArrayType];
constraint c3 { foreach (data[i]) data[i] == 0; }
function new();
IndexArrayType idx;
idx = 0;
data[idx] = 32'd75;
endfunction
function void self_check();
foreach (data[i]) begin
if (data[i] != 0) $stop;
end
endfunction
endclass
module t_constraint_assoc_array_others;
AssocArrayEnum enum_arr;
AssocArrayPackedStruct packed_arr;
AssocArrayArrayIndex array_arr;
int success = 0;
initial begin
// Create instances of the classes
enum_arr = new();
packed_arr = new();
array_arr = new();
// Randomization and self-check
success = enum_arr.randomize();
if (success != 1) $stop;
enum_arr.self_check();
success = packed_arr.randomize();
if (success != 1) $stop;
packed_arr.self_check();
success = array_arr.randomize();
if (success != 1) $stop;
array_arr.self_check();
$write("*-* All Finished *-*");
$finish;
end
endmodule