Support associative array basic constrained randomization (#5658) (#5670)

This commit is contained in:
Yilou Wang 2024-12-12 17:31:54 +01:00 committed by GitHub
parent 32f9cf072b
commit 54ef9ad31c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 441 additions and 101 deletions

View File

@ -464,10 +464,10 @@ bool VlRandomizer::parseSolution(std::iostream& f) {
const size_t start = hex_index.find_first_not_of(" ");
if (start == std::string::npos || hex_index.substr(start, 2) != "#x") {
VL_FATAL_MT(__FILE__, __LINE__, "randomize",
"Error: hex_index contains invalid format");
"hex_index contains invalid format");
continue;
}
const int index = std::stoi(hex_index.substr(start + 2), nullptr, 16);
const long long index = std::stoll(hex_index.substr(start + 2), nullptr, 16);
oss << "[" << index << "]";
}
const std::string indexed_name = oss.str();
@ -481,7 +481,7 @@ bool VlRandomizer::parseSolution(std::iostream& f) {
idx = ss.str();
} else {
VL_FATAL_MT(__FILE__, __LINE__, "randomize",
"Error: indexed_name not found in m_arr_vars");
"indexed_name not found in m_arr_vars");
}
}
varr.set(idx, value);

View File

@ -29,6 +29,7 @@
#include <iostream>
#include <ostream>
//=============================================================================
// VlRandomExpr and subclasses represent expressions for the constraint solver.
class ArrayInfo final {
@ -38,12 +39,15 @@ public:
void* const m_datap; // Reference to the array variable data
const int m_index; // Flattened (1D) index of the array element
const std::vector<size_t> m_indices; // Multi-dimensional indices of the array element
const std::vector<size_t> m_idxWidths; // Multi-dimensional indices' bit widths
ArrayInfo(const std::string& name, void* datap, int index, const std::vector<size_t>& indices)
ArrayInfo(const std::string& name, void* datap, int index, const std::vector<size_t>& indices,
const std::vector<size_t>& idxWidths)
: m_name(name)
, m_datap(datap)
, m_index(index)
, m_indices(indices) {}
, m_indices(indices)
, m_idxWidths(idxWidths) {}
};
using ArrayInfoMap = std::map<std::string, std::shared_ptr<const ArrayInfo>>;
@ -90,25 +94,30 @@ public:
return count;
}
};
template <typename T>
class VlRandomQueueVar final : public VlRandomVar {
class VlRandomArrayVarTemplate final : public VlRandomVar {
public:
VlRandomQueueVar(const char* name, int width, void* datap, int dimension,
VlRandomArrayVarTemplate(const char* name, int width, void* datap, int dimension,
std::uint32_t randModeIdx)
: VlRandomVar{name, width, datap, dimension, randModeIdx} {}
void* datap(int idx) const override {
const std::string indexed_name = name() + std::to_string(idx);
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) return it->second->m_datap;
return &static_cast<T*>(VlRandomVar::datap(idx))->atWrite(idx);
if (it != m_arrVarsRefp->end()) {
return it->second->m_datap;
} else {
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
return nullptr;
}
void emitSelect(std::ostream& s, const std::vector<size_t>& indices) const {
}
void emitSelect(std::ostream& s, const std::vector<size_t>& indices,
const std::vector<size_t>& idxWidths) const {
for (size_t idx = 0; idx < indices.size(); ++idx) s << "(select ";
s << name();
for (size_t idx = 0; idx < indices.size(); ++idx) {
s << " #x";
for (int j = 28; j >= 0; j -= 4) {
const size_t bit_width = idxWidths[idx];
for (int j = bit_width - 4; j >= 0; j -= 4) {
s << "0123456789abcdef"[(indices[idx] >> j) & 0xf];
}
s << ")";
@ -121,15 +130,28 @@ public:
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) {
const std::vector<size_t>& indices = it->second->m_indices;
emitSelect(s, indices);
const std::vector<size_t>& idxWidths = it->second->m_idxWidths;
emitSelect(s, indices, idxWidths);
} else {
VL_FATAL_MT(__FILE__, __LINE__, "randomize",
"indexed_name not found in m_arr_vars");
}
}
}
void emitType(std::ostream& s) const override {
const std::string indexed_name = name() + std::to_string(0);
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) {
const std::vector<size_t>& idxWidths = it->second->m_idxWidths;
if (dimension() > 0) {
for (int i = 0; i < dimension(); ++i) s << "(Array (_ BitVec 32) ";
for (int i = 0; i < dimension(); ++i) {
s << "(Array (_ BitVec " << idxWidths[i] << ") ";
}
s << "(_ BitVec " << width() << ")";
for (int i = 0; i < dimension(); ++i) s << ")";
for (int i = 0; i < dimension(); ++i) { s << ")"; }
}
} else {
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
}
}
int totalWidth() const override {
@ -144,66 +166,10 @@ public:
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) {
const std::vector<size_t>& indices = it->second->m_indices;
emitSelect(s, indices);
}
s << ')';
}
};
template <typename T>
class VlRandomArrayVar final : public VlRandomVar {
public:
VlRandomArrayVar(const char* name, int width, void* datap, int dimension,
std::uint32_t randModeIdx)
: VlRandomVar{name, width, datap, dimension, randModeIdx} {}
void* datap(int idx) const override {
const std::string indexed_name = name() + std::to_string(idx);
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) return it->second->m_datap;
return &static_cast<T*>(VlRandomVar::datap(idx))->operator[](idx);
}
void emitSelect(std::ostream& s, const std::vector<size_t>& indices) const {
for (size_t idx = 0; idx < indices.size(); ++idx) s << "(select ";
s << name();
for (size_t idx = 0; idx < indices.size(); ++idx) {
s << " #x";
for (int j = 28; j >= 0; j -= 4) {
s << "0123456789abcdef"[(indices[idx] >> j) & 0xf];
}
s << ")";
}
}
void emitGetValue(std::ostream& s) const override {
const int elementCounts = countMatchingElements(*m_arrVarsRefp, name());
for (int i = 0; i < elementCounts; i++) {
const std::string indexed_name = name() + std::to_string(i);
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) {
const std::vector<size_t>& indices = it->second->m_indices;
emitSelect(s, indices);
}
}
}
void emitType(std::ostream& s) const override {
if (dimension() > 0) {
for (int i = 0; i < dimension(); ++i) s << "(Array (_ BitVec 32) ";
s << "(_ BitVec " << width() << ")";
for (int i = 0; i < dimension(); ++i) s << ")";
}
}
int totalWidth() const override {
const int elementCounts = countMatchingElements(*m_arrVarsRefp, name());
return width() * elementCounts;
}
void emitExtract(std::ostream& s, int i) const override {
const int j = i / width();
i = i % width();
s << " ((_ extract " << i << ' ' << i << ')';
const std::string indexed_name = name() + std::to_string(j);
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) {
const std::vector<size_t>& indices = it->second->m_indices;
emitSelect(s, indices);
const std::vector<size_t>& idxWidths = it->second->m_idxWidths;
emitSelect(s, indices, idxWidths);
} else {
VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
}
s << ')';
}
@ -217,6 +183,7 @@ class VlRandomizer final {
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
// variables
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
std::map<size_t, std::string> seen_values; // Record String Index to avoid conflicts
const VlQueue<CData>* m_randmode; // rand_mode state;
// PRIVATE METHODS
@ -231,6 +198,47 @@ public:
// METHODS
// Finds the next solution satisfying the constraints
bool next(VlRNG& rngr);
template <typename T_Key>
typename std::enable_if<std::is_integral<T_Key>::value>::type
process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index,
const std::string& base_name, size_t& idx_width) {
integral_index = static_cast<size_t>(key);
indexed_name = base_name + "[" + std::to_string(integral_index) + "]";
idx_width = sizeof(T_Key) * 8;
}
template <typename T_Key>
typename std::enable_if<std::is_same<T_Key, std::string>::value>::type
process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index,
const std::string& base_name, size_t& idx_width) {
integral_index = string_to_integral(key);
indexed_name = base_name + "[" + std::to_string(integral_index) + "]";
idx_width = 64; // 64-bit mask
}
template <typename T_Key>
typename std::enable_if<!std::is_integral<T_Key>::value
&& !std::is_same<T_Key, std::string>::value>::type
process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index,
const std::string& base_name, size_t& idx_width) {
VL_FATAL_MT(__FILE__, __LINE__, "randomize",
"Unsupported: Only integral and string index of associative array is "
"supported currently.");
}
uint64_t string_to_integral(const std::string& str) {
uint64_t result = 0;
for (char c : str) { result = (result << 8) | static_cast<uint64_t>(c); }
#ifdef VL_DEBUG
if (seen_values.count(result) > 0 && seen_values[result] != str)
VL_WARN_MT(__FILE__, __LINE__, "randomize",
"Conflict detected: Different strings mapped to the same 64-bit index.");
seen_values[result] = str;
#endif
return result;
}
template <typename T>
void write_var(T& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
@ -243,25 +251,38 @@ public:
void write_var(VlQueue<T>& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) != m_vars.end()) return;
m_vars[name] = std::make_shared<const VlRandomQueueVar<VlQueue<T>>>(
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
name, width, &var, dimension, randmodeIdx);
if (dimension > 0) {
idx = 0;
record_arr_table(var, name, dimension, {});
record_arr_table(var, name, dimension, {}, {});
}
}
template <typename T, std::size_t N_Depth>
void write_var(VlUnpacked<T, N_Depth>& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) != m_vars.end()) return;
m_vars[name] = std::make_shared<const VlRandomArrayVar<VlUnpacked<T, N_Depth>>>(
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
name, width, &var, dimension, randmodeIdx);
if (dimension > 0) {
idx = 0;
record_arr_table(var, name, dimension, {});
record_arr_table(var, name, dimension, {}, {});
}
}
int idx = 0;
template <typename T_Key, typename T_Value>
void write_var(VlAssocArray<T_Key, T_Value>& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
if (m_vars.find(name) != m_vars.end()) return;
m_vars[name]
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
name, width, &var, dimension, randmodeIdx);
if (dimension > 0) {
idx = 0;
record_arr_table(var, name, dimension, {}, {});
}
}
int idx;
std::string generateKey(const std::string& name, int idx) {
if (!name.empty() && name[0] == '\\') {
const size_t space_pos = name.find(' ');
@ -272,45 +293,61 @@ public:
return (bracket_pos != std::string::npos ? name.substr(0, bracket_pos) : name)
+ std::to_string(idx);
}
template <typename T>
void record_arr_table(T& var, const std::string name, int dimension,
std::vector<size_t> indices) {
std::vector<size_t> indices, std::vector<size_t> idxWidths) {
const std::string key = generateKey(name, idx);
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, idx, indices);
idx += 1;
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, idx, indices, idxWidths);
++idx;
}
template <typename T>
void record_arr_table(VlQueue<T>& var, const std::string name, int dimension,
std::vector<size_t> indices) {
std::vector<size_t> indices, std::vector<size_t> idxWidths) {
if ((dimension > 0) && (var.size() != 0)) {
idxWidths.push_back(32);
for (size_t i = 0; i < var.size(); ++i) {
const std::string indexed_name = name + "[" + std::to_string(i) + "]";
indices.push_back(i);
record_arr_table(var.atWrite(i), indexed_name, dimension - 1, indices);
record_arr_table(var.atWrite(i), indexed_name, dimension - 1, indices, idxWidths);
indices.pop_back();
}
} else {
const std::string key = generateKey(name, idx);
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, idx, indices);
++idx;
}
}
template <typename T, std::size_t N_Depth>
void record_arr_table(VlUnpacked<T, N_Depth>& var, const std::string name, int dimension,
std::vector<size_t> indices) {
std::vector<size_t> indices, std::vector<size_t> idxWidths) {
if ((dimension > 0) && (N_Depth != 0)) {
idxWidths.push_back(32);
for (size_t i = 0; i < N_Depth; ++i) {
const std::string indexed_name = name + "[" + std::to_string(i) + "]";
indices.push_back(i);
record_arr_table(var.operator[](i), indexed_name, dimension - 1, indices);
record_arr_table(var.operator[](i), indexed_name, dimension - 1, indices,
idxWidths);
indices.pop_back();
}
} else {
const std::string key = generateKey(name, idx);
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, idx, indices);
idx += 1;
}
}
template <typename T_Key, typename T_Value>
void record_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string name, int dimension,
std::vector<size_t> indices, std::vector<size_t> idxWidths) {
if ((dimension > 0) && (var.size() != 0)) {
for (auto it = var.begin(); it != var.end(); ++it) {
const T_Key& key = it->first;
const T_Value& value = it->second;
std::string indexed_name;
size_t integral_index;
size_t idx_width;
process_key(key, indexed_name, integral_index, name, idx_width);
idxWidths.push_back(idx_width);
indices.push_back(integral_index);
record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths);
idxWidths.pop_back();
indices.pop_back();
}
}
}
void hard(std::string&& constraint);
void clear();
void set_randmode(const VlQueue<CData>& randmode) { m_randmode = &randmode; }

View File

@ -4191,6 +4191,7 @@ public:
}
string emitVerilog() override { return "%k(%l%f[%r])"; }
string emitC() override { return "%li%k[%ri]"; }
string emitSMT() const override { return "(select %l %r)"; }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return false; }
bool cleanRhs() const override { return true; }

View File

@ -641,7 +641,7 @@ class ConstraintExprVisitor final : public VNVisitor {
"write_var"};
uint32_t dimension = 0;
if (VN_IS(varp->dtypep(), UnpackArrayDType) || VN_IS(varp->dtypep(), DynArrayDType)
|| VN_IS(varp->dtypep(), QueueDType)) {
|| VN_IS(varp->dtypep(), QueueDType) || VN_IS(varp->dtypep(), AssocArrayDType)) {
const std::pair<uint32_t, uint32_t> dims
= varp->dtypep()->dimensions(/*includeBasic=*/true);
const uint32_t unpackedDimensions = dims.second;
@ -656,7 +656,7 @@ class ConstraintExprVisitor final : public VNVisitor {
size_t width = varp->width();
AstNodeDType* tmpDtypep = varp->dtypep();
while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType)
|| VN_IS(tmpDtypep, QueueDType))
|| VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType))
tmpDtypep = tmpDtypep->subDTypep();
width = tmpDtypep->width();
methodp->addPinsp(
@ -724,6 +724,44 @@ class ConstraintExprVisitor final : public VNVisitor {
editSMT(nodep, nodep->fromp(), lsbp, msbp);
}
void visit(AstAssocSel* nodep) override {
if (editFormat(nodep)) return;
FileLine* const fl = nodep->fileline();
if (VN_IS(nodep->bitp(), CvtPackString)) {
// Extract and truncate the string index to fit within 64 bits
AstCvtPackString* const stringp = VN_AS(nodep->bitp(), CvtPackString);
VNRelinker handle;
AstNodeExpr* const strIdxp = new AstSFormatF{
fl, "#x%16x", false,
new AstAnd{fl, stringp->lhsp()->unlinkFrBack(&handle),
new AstConst(fl, AstConst::Unsized64{}, 0xFFFFFFFFFFFFFFFF)}};
handle.relink(strIdxp);
editSMT(nodep, nodep->fromp(), strIdxp);
} 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 if (actual_width <= 32) {
fmt = "#x%8x";
} else if (actual_width <= 64) {
fmt = "#x%16x";
} else {
nodep->v3warn(CONSTRAINTIGN,
"Unsupported: Associative array index "
"widths of more than 64 bits during constraint randomization.");
return;
}
AstNodeExpr* const idxp
= new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)};
handle.relink(idxp);
editSMT(nodep, nodep->fromp(), idxp);
}
}
void visit(AstArraySel* nodep) override {
if (editFormat(nodep)) return;
FileLine* const fl = nodep->fileline();

View File

@ -0,0 +1,9 @@
%Warning-CONSTRAINTIGN: t/t_constraint_assoc_arr_bad.v:14:22: Unsupported: Associative array index widths of more than 64 bits during constraint randomization.
14 | bit_index_arr[79'd66] == 65;
| ^
... 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.
%Warning-CONSTRAINTIGN: t/t_constraint_assoc_arr_bad.v:15:24: Unsupported: Associative array index widths of more than 64 bits during constraint randomization.
15 | logic_index_arr[65'd3] == 70;
| ^
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/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')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,38 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by PlanV GmbH.
// SPDX-License-Identifier: CC0-1.0
class AssocArrayWarningTest;
rand int bit_index_arr [bit[78:0]];
rand int logic_index_arr [logic[64:0]];
constraint c {
bit_index_arr[79'd66] == 65;
logic_index_arr[65'd3] == 70;
}
function new();
bit_index_arr = '{79'd66:0};
logic_index_arr = '{65'd3:0};
endfunction
endclass
module t_constraint_assoc_arr_bad;
AssocArrayWarningTest test_obj;
initial begin
test_obj = new();
repeat(2) begin
int success;
success = test_obj.randomize();
if (success != 1) $stop;
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,180 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by PlanV GmbH.
// SPDX-License-Identifier: CC0-1.0
class constrained_associative_array_basic;
rand int int_index_arr [int];
rand int string_index_arr [string];
/* verilator lint_off SIDEEFFECT */
// Constraints for both arrays
constraint int_index_constraints {
foreach (int_index_arr[i]) int_index_arr[i] inside {10, 20, 30, 40, 50};
}
constraint string_index_constraints {
string_index_arr["Alice"] == 35;
string_index_arr["Bob"] inside {50, 60};
string_index_arr["Charlie"] > 25;
}
// Constructor to initialize arrays
function new();
int_index_arr = '{1: 0, 8: 0, 7: 0};
string_index_arr = '{"Alice": 25, "Bob": 50, "Charlie": 45};
endfunction
// Function to check and display the arrays
function void self_check();
foreach (int_index_arr[i]) begin
if (!(int_index_arr[i] inside {10, 20, 30, 40, 50})) $stop;
end
foreach (string_index_arr[name]) begin
if ((name == "Alice" && string_index_arr[name] != 35) ||
(name == "Bob" && !(string_index_arr[name] inside {50, 60})) ||
(name == "Charlie" && string_index_arr[name] <= 25)) $stop;
end
endfunction
endclass
class constrained_1d_associative_array;
rand int string_index_arr [string];
rand int int_index_arr [int];
rand int shortint_index_arr [shortint];
rand int longint_index_arr[longint];
rand int byte_index_arr [byte];
rand int bit_index_arr [bit[5:0]];
rand int logic_index_arr [logic[3:0]];
rand int bit_index_arr_1 [bit[55:0]];
// Constraints
constraint associative_array_constraints {
string_index_arr["key1"] == 100;
string_index_arr["key2"] inside {200, 300, 400};
int_index_arr[40000] + int_index_arr[2000000000] == 2;
shortint_index_arr[2000] == 200;
longint_index_arr[64'd4000000000] == 300;
byte_index_arr[8'd255] == 50;
bit_index_arr[6'd30] - bit_index_arr_1[56'd66] == 3;
logic_index_arr[4'b0011] == 70;
}
function new();
string_index_arr = '{"key1":0, "key2":0};
int_index_arr = '{40000:0, 2000000000:0};
shortint_index_arr = '{2000:0};
longint_index_arr = '{64'd4000000000:0};
byte_index_arr = '{8'd255:0};
bit_index_arr = '{6'd30:0};
bit_index_arr_1 = '{56'd66:0};
logic_index_arr = '{4'd3:0};
endfunction
function void self_check();
if (string_index_arr["key1"] != 100) $stop;
if (!(string_index_arr["key2"] inside {200, 300, 400})) $stop;
if ((int_index_arr[40000] + int_index_arr[2000000000]) != 2) $stop;
if (shortint_index_arr[2000] != 200) $stop;
if (longint_index_arr[64'd4000000000] != 300) $stop;
if (byte_index_arr[8'd255] != 50) $stop;
if (bit_index_arr[6'd30] - bit_index_arr_1[56'd66] != 3) $stop;
if (logic_index_arr[4'd3] != 70) $stop;
endfunction
function void debug_display();
$display("string_index_arr[\"key1\"] = %0d", string_index_arr["key1"]);
$display("string_index_arr[\"key2\"] = %0d", string_index_arr["key2"]);
$display("int_index_arr[40000] = %0d", int_index_arr[40000]);
$display("int_index_arr[2000000000] = %0d", int_index_arr[2000000000]);
$display("shortint_index_arr[2000] = %0d", shortint_index_arr[2000]);
$display("longint_index_arr[4000000000] = %0d", longint_index_arr[64'd4000000000]);
$display("byte_index_arr[255] = %0d", byte_index_arr[8'd255]);
$display("bit_index_arr[30] = %0d", bit_index_arr[6'd30]);
$display("bit_index_arr_1[66] = %0d", bit_index_arr_1[56'd66]);
$display("logic_index_arr[3] = %0d", logic_index_arr[4'd3]);
endfunction
endclass
class constrained_2d_associative_array;
rand int string_int_index_arr [string][int];
rand int int_bit_index_arr [int][bit[5:0]];
rand int string_bit_index_arr [string][bit[7:0]];
rand int unpacked_assoc_array_2d [string][2];
// Constraints
constraint associative_array_constraints {
string_int_index_arr["key1"][2000] == 100;
string_int_index_arr["key2"][3000] inside {200, 300, 400};
int_bit_index_arr[40000][6'd30] == 60;
int_bit_index_arr[50000][6'd40] inside {100, 200};
string_bit_index_arr["key3"][8'd100] == 150;
string_bit_index_arr["key4"][8'd200] inside {250, 350};
unpacked_assoc_array_2d["key5"][0] == 7;
}
function new();
string_int_index_arr = '{"key1":'{2000:0}, "key2":'{3000:0}};
int_bit_index_arr = '{40000:'{6'd30:0}, 50000:'{6'd40:0}};
string_bit_index_arr = '{"key3":'{8'd100:0}, "key4":'{8'd200:0}};
unpacked_assoc_array_2d["key5"][0] = 0;
unpacked_assoc_array_2d["key5"][1] = 0;
endfunction
function void self_check();
if (string_int_index_arr["key1"][2000] != 100) $stop;
if (!(string_int_index_arr["key2"][3000] inside {200, 300, 400})) $stop;
if (int_bit_index_arr[40000][6'd30] != 60) $stop;
if (!(int_bit_index_arr[50000][6'd40] inside {100, 200})) $stop;
if (string_bit_index_arr["key3"][8'd100] != 150) $stop;
if (!(string_bit_index_arr["key4"][8'd200] inside {250, 350})) $stop;
if (unpacked_assoc_array_2d["key5"][0] != 7) $stop;
endfunction
function void debug_display();
$display("string_int_index_arr[\"key1\"][2000] = %0d", string_int_index_arr["key1"][2000]);
$display("string_int_index_arr[\"key2\"][3000] = %0d", string_int_index_arr["key2"][3000]);
$display("int_bit_index_arr[40000][30] = %0d", int_bit_index_arr[40000][6'd30]);
$display("int_bit_index_arr[50000][40] = %0d", int_bit_index_arr[50000][6'd40]);
$display("string_bit_index_arr[\"key3\"][100] = %0d", string_bit_index_arr["key3"][8'd100]);
$display("string_bit_index_arr[\"key4\"][200] = %0d", string_bit_index_arr["key4"][8'd200]);
$display("unpacked_assoc_array_2d[\"key5\"][0] = %0d", unpacked_assoc_array_2d["key5"][0]);
endfunction
/* verilator lint_off SIDEEFFECT */
endclass
module t_constraint_assoc_arr_basic;
constrained_associative_array_basic my_array;
constrained_1d_associative_array my_1d_array;
constrained_2d_associative_array my_2d_array;
int success;
initial begin
my_array = new();
success = my_array.randomize();
if (success == 0) $stop;
my_array.self_check();
my_1d_array = new();
success = my_1d_array.randomize();
if (success == 0) $stop;
my_1d_array.self_check();
my_1d_array = new();
success = my_1d_array.randomize();
if (success == 0) $stop;
my_1d_array.self_check();
// my_1d_array.debug_display();
// my_2d_array.debug_display();
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

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

@ -125,7 +125,7 @@ class con_rand_3d_array_test;
endclass
module t_randomize_array_constraints;
module t_constraint_unpacked_array;
con_rand_1d_array_test rand_test_1;
con_rand_2d_array_test rand_test_2;
con_rand_3d_array_test rand_test_3;