parent
550cf4462d
commit
6fc9089a77
|
|
@ -367,7 +367,26 @@ void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) {
|
|||
}
|
||||
|
||||
bool VlRandomizer::next(VlRNG& rngr) {
|
||||
if (m_vars.empty()) return true;
|
||||
if (m_vars.empty() && m_unique_arrays.empty()) return true;
|
||||
for (const std::string& baseName : m_unique_arrays) {
|
||||
const auto it = m_vars.find(baseName);
|
||||
|
||||
// Look up the actual size we stored earlier
|
||||
// const uint32_t size = m_unique_array_sizes[baseName];
|
||||
const uint32_t size = m_unique_array_sizes.at(baseName);
|
||||
|
||||
if (it != m_vars.end()) {
|
||||
std::string distinctExpr = "(__Vbv (distinct";
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
char hexIdx[12];
|
||||
sprintf(hexIdx, "#x%08x", i);
|
||||
distinctExpr += " (select " + it->first + " " + hexIdx + ")";
|
||||
}
|
||||
distinctExpr += "))";
|
||||
m_constraints.push_back(distinctExpr);
|
||||
}
|
||||
}
|
||||
|
||||
std::iostream& os = getSolver();
|
||||
if (!os) return false;
|
||||
|
||||
|
|
@ -384,6 +403,7 @@ bool VlRandomizer::next(VlRNG& rngr) {
|
|||
var.second->emitType(os);
|
||||
os << ")\n";
|
||||
}
|
||||
|
||||
for (const std::string& constraint : m_constraints) {
|
||||
os << "(assert (= #b1 " << constraint << "))\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,7 +195,6 @@ public:
|
|||
};
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// Object holding constraints and variable references.
|
||||
class VlRandomizer VL_NOT_FINAL {
|
||||
// MEMBERS
|
||||
|
|
@ -205,6 +204,8 @@ class VlRandomizer VL_NOT_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::vector<std::string> m_unique_arrays;
|
||||
std::map<std::string, uint32_t> m_unique_array_sizes;
|
||||
const VlQueue<CData>* m_randmodep = nullptr; // rand_mode state;
|
||||
int m_index = 0; // Internal counter for key generation
|
||||
|
||||
|
|
@ -317,10 +318,10 @@ public:
|
|||
}
|
||||
|
||||
// --- write_var to register variables ---
|
||||
|
||||
// Register scalar variable (non-struct, basic type)
|
||||
template <typename T>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value && !IsVlUnpacked<T>::value,
|
||||
void>::type
|
||||
write_var(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;
|
||||
|
|
@ -358,21 +359,23 @@ public:
|
|||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
}
|
||||
|
||||
// Register unpacked array of non-struct types
|
||||
template <typename T, std::size_t N_Depth>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlUnpacked<T, N_Depth>& var, int width, const char* name, int dimension,
|
||||
write_var(VlUnpacked<T, N_Depth>& var, uint32_t width, const std::string& name,
|
||||
uint32_t 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<VlUnpacked<T, N_Depth>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
|
||||
if (dimension > 0) {
|
||||
m_index = 0;
|
||||
record_arr_table(var, name, dimension, {}, {});
|
||||
}
|
||||
}
|
||||
|
||||
// Register unpacked array of structs
|
||||
template <typename T, std::size_t N_Depth>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
|
|
@ -416,6 +419,12 @@ public:
|
|||
++m_index;
|
||||
}
|
||||
|
||||
// This is the "Sender" API for the generated code
|
||||
void rand_unique(const std::string& name, uint32_t size) {
|
||||
m_unique_arrays.push_back(name);
|
||||
m_unique_array_sizes[name] = size;
|
||||
}
|
||||
|
||||
// Recursively record all elements in an unpacked array
|
||||
template <typename T, std::size_t N_Depth>
|
||||
void record_arr_table(VlUnpacked<T, N_Depth>& var, const std::string& name, int dimension,
|
||||
|
|
|
|||
|
|
@ -1612,6 +1612,11 @@ private:
|
|||
return a != b;
|
||||
}
|
||||
};
|
||||
// Trait to detect VlUnpacked types
|
||||
template <typename T>
|
||||
struct IsVlUnpacked : std::false_type {};
|
||||
template <typename T, std::size_t N>
|
||||
struct IsVlUnpacked<VlUnpacked<T, N>> : std::true_type {};
|
||||
|
||||
template <typename T_Value, std::size_t N_Depth>
|
||||
std::string VL_TO_STRING(const VlUnpacked<T_Value, N_Depth>& obj) {
|
||||
|
|
|
|||
|
|
@ -787,6 +787,7 @@ public:
|
|||
RANDOMIZER_CLEARCONSTRAINTS,
|
||||
RANDOMIZER_CLEARALL,
|
||||
RANDOMIZER_HARD,
|
||||
RANDOMIZER_UNIQUE,
|
||||
RANDOMIZER_WRITE_VAR,
|
||||
RNG_GET_RANDSTATE,
|
||||
RNG_SET_RANDSTATE,
|
||||
|
|
@ -916,6 +917,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
|
|||
{RANDOMIZER_CLEARCONSTRAINTS, "clearConstraints", false}, \
|
||||
{RANDOMIZER_CLEARALL, "clearAll", false}, \
|
||||
{RANDOMIZER_HARD, "hard", false}, \
|
||||
{RANDOMIZER_UNIQUE, "rand_unique", false}, \
|
||||
{RANDOMIZER_WRITE_VAR, "write_var", false}, \
|
||||
{RNG_GET_RANDSTATE, "__Vm_rng.get_randstate", true}, \
|
||||
{RNG_SET_RANDSTATE, "__Vm_rng.set_randstate", false}, \
|
||||
|
|
|
|||
|
|
@ -725,6 +725,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
// AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module
|
||||
// VNuser3InUse m_inuser3; (Allocated for use in RandomizeVisitor)
|
||||
|
||||
AstClass* const m_classp;
|
||||
AstNodeFTask* const m_inlineInitTaskp; // Method to add write_var calls to
|
||||
// (may be null, then new() is used)
|
||||
AstVar* const m_genp; // VlRandomizer variable of the class
|
||||
|
|
@ -1336,9 +1337,83 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
void visit(AstConstraintUnique* nodep) override {
|
||||
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (unsupported)");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
if (!m_classp) return;
|
||||
|
||||
FileLine* const fl = nodep->fileline();
|
||||
|
||||
AstNodeFTask* const initTaskp = VN_AS(m_memberMap.findMember(m_classp, "new"), NodeFTask);
|
||||
if (!initTaskp) return;
|
||||
|
||||
AstVar* const genVarp = VN_AS(m_classp->user3p(), Var);
|
||||
if (!genVarp) return;
|
||||
|
||||
for (AstNode* itemp = nodep->rangesp(); itemp; itemp = itemp->nextp()) {
|
||||
if (AstVarRef* const varRefp = VN_CAST(itemp, VarRef)) {
|
||||
AstVar* const varp = varRefp->varp();
|
||||
|
||||
AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
|
||||
if (AstUnpackArrayDType* const up = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
AstRange* const rangep = up->rangep();
|
||||
if (!rangep || !VN_IS(rangep->leftp(), Const)
|
||||
|| !VN_IS(rangep->rightp(), Const)) {
|
||||
nodep->v3warn(
|
||||
CONSTRAINTIGN,
|
||||
"Unsupported: Unique constraint on other than static arrays");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure it is ONLY 1-D by checking that the sub-type is NOT an array/queue
|
||||
// We skip refs (typedefs) to see the actual underlying type
|
||||
AstNodeDType* const subp = up->subDTypep()->skipRefp();
|
||||
if (VN_IS(subp, NodeArrayDType) || VN_IS(subp, QueueDType)
|
||||
|| VN_IS(subp, DynArrayDType)) {
|
||||
nodep->v3warn(
|
||||
CONSTRAINTIGN,
|
||||
"Unsupported: Unique constraint on other than static arrays");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
nodep->v3warn(CONSTRAINTIGN,
|
||||
"Unsupported: Unique constraint on other than static arrays");
|
||||
continue;
|
||||
}
|
||||
|
||||
AstCMethodHard* const wCallp = new AstCMethodHard{
|
||||
fl, new AstVarRef{fl, genVarp, VAccess::READ}, VCMethod::RANDOMIZER_WRITE_VAR};
|
||||
wCallp->addPinsp(new AstVarRef{fl, varp, VAccess::READ});
|
||||
wCallp->addPinsp(new AstConst{fl, AstConst::Unsized64{},
|
||||
static_cast<uint64_t>(varp->dtypep()->width())});
|
||||
wCallp->addPinsp(new AstConst{fl, AstConst::String{}, varp->name()});
|
||||
wCallp->addPinsp(new AstConst{fl, 1}); // Dimension
|
||||
|
||||
wCallp->dtypeSetVoid();
|
||||
initTaskp->addStmtsp(new AstStmtExpr{fl, wCallp});
|
||||
|
||||
uint32_t arraySize = 0;
|
||||
if (AstUnpackArrayDType* const adtypep
|
||||
= VN_CAST(varp->dtypep(), UnpackArrayDType)) {
|
||||
arraySize = adtypep->elementsConst();
|
||||
}
|
||||
if (arraySize > 100) {
|
||||
nodep->v3warn(CONSTRAINTIGN,
|
||||
"Unsupported: Unique constraint on static arrays of size > 100");
|
||||
continue;
|
||||
}
|
||||
|
||||
AstNodeExpr* const uPins = new AstConst{fl, AstConst::String{}, varp->name()};
|
||||
uPins->addNext(new AstConst{fl, arraySize});
|
||||
|
||||
AstCMethodHard* const uCallp
|
||||
= new AstCMethodHard{fl, new AstVarRef{fl, genVarp, VAccess::READ},
|
||||
VCMethod::RANDOMIZER_UNIQUE, uPins};
|
||||
uCallp->dtypep(nodep->findVoidDType());
|
||||
initTaskp->addStmtsp(new AstStmtExpr{fl, uCallp});
|
||||
}
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
||||
void visit(AstConstraintExpr* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
if (m_wantSingle) {
|
||||
|
|
@ -1440,10 +1515,11 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit ConstraintExprVisitor(VMemberMap& memberMap, AstNode* nodep,
|
||||
explicit ConstraintExprVisitor(AstClass* classp, VMemberMap& memberMap, AstNode* nodep,
|
||||
AstNodeFTask* inlineInitTaskp, AstVar* genp,
|
||||
AstVar* randModeVarp, std::set<std::string>& writtenVars)
|
||||
: m_inlineInitTaskp{inlineInitTaskp}
|
||||
: m_classp{classp}
|
||||
, m_inlineInitTaskp{inlineInitTaskp}
|
||||
, m_genp{genp}
|
||||
, m_randModeVarp{randModeVarp}
|
||||
, m_memberMap{memberMap}
|
||||
|
|
@ -2579,8 +2655,8 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
resizeAllTaskp->addStmtsp(resizeTaskRefp->makeStmt());
|
||||
}
|
||||
|
||||
ConstraintExprVisitor{m_memberMap, constrp->itemsp(), nullptr,
|
||||
genp, randModeVarp, m_writtenVars};
|
||||
ConstraintExprVisitor{classp, m_memberMap, constrp->itemsp(), nullptr,
|
||||
genp, randModeVarp, m_writtenVars};
|
||||
if (constrp->itemsp()) {
|
||||
taskp->addStmtsp(wrapIfConstraintMode(
|
||||
nodep, constrp, constrp->itemsp()->unlinkFrBackWithNext()));
|
||||
|
|
@ -2833,8 +2909,8 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
AstNode* const capturedTreep = withp->exprp()->unlinkFrBackWithNext();
|
||||
randomizeFuncp->addStmtsp(capturedTreep);
|
||||
{
|
||||
ConstraintExprVisitor{m_memberMap, capturedTreep, randomizeFuncp,
|
||||
stdrand, nullptr, m_writtenVars};
|
||||
ConstraintExprVisitor{nullptr, m_memberMap, capturedTreep, randomizeFuncp,
|
||||
stdrand, nullptr, m_writtenVars};
|
||||
}
|
||||
AstCExpr* const solverCallp = new AstCExpr{fl};
|
||||
solverCallp->dtypeSetBit();
|
||||
|
|
@ -2961,8 +3037,8 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
AstNode* const capturedTreep = withp->exprp()->unlinkFrBackWithNext();
|
||||
randomizeFuncp->addStmtsp(capturedTreep);
|
||||
{
|
||||
ConstraintExprVisitor{m_memberMap, capturedTreep, randomizeFuncp,
|
||||
localGenp, randModeVarp, m_writtenVars};
|
||||
ConstraintExprVisitor{classp, m_memberMap, capturedTreep, randomizeFuncp,
|
||||
localGenp, randModeVarp, m_writtenVars};
|
||||
}
|
||||
|
||||
// Call the solver and set return value
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
%Warning-CONSTRAINTIGN: t/t_constraint_unsup_unq_arr.v:19:5: Unsupported: Unique constraint on static arrays of size > 100
|
||||
: ... note: In instance 't'
|
||||
19 | unique {uniq_val_arr_400};
|
||||
| ^~~~~~
|
||||
... 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_unsup_unq_arr.v:22:5: Unsupported: Unique constraint on other than static arrays
|
||||
: ... note: In instance 't'
|
||||
22 | unique {uniq_val_darr};
|
||||
| ^~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_unsup_unq_arr.v:23:5: Unsupported: Unique constraint on other than static arrays
|
||||
: ... note: In instance 't'
|
||||
23 | unique {uniq_val_hash};
|
||||
| ^~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_unsup_unq_arr.v:24:5: Unsupported: Unique constraint on other than static arrays
|
||||
: ... note: In instance 't'
|
||||
24 | unique {uniq_val_queue};
|
||||
| ^~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_constraint_unsup_unq_arr.v:25:5: Unsupported: Unique constraint on other than static arrays
|
||||
: ... note: In instance 't'
|
||||
25 | unique {uniq_val_arr_mda};
|
||||
| ^~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -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('vlt')
|
||||
|
||||
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by AsFigo.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
class UniqueMultipleArray;
|
||||
rand bit [15:0] uniq_val_arr[4];
|
||||
rand bit [15:0] uniq_val_arr_400[400];
|
||||
rand bit [15:0] uniq_val_arr_mda[4][];
|
||||
rand bit [15:0] uniq_val_darr[];
|
||||
rand bit [15:0] uniq_val_hash[int];
|
||||
rand bit [15:0] uniq_val_queue[$];
|
||||
rand bit b1;
|
||||
rand int array[2]; // 2,4,6 // TODO: add rand when supported
|
||||
|
||||
// Constraint to ensure the elements in the array are unique
|
||||
constraint unique_c {
|
||||
unique {uniq_val_arr}; // Ensure unique values in the array
|
||||
unique {uniq_val_arr_400}; // Ensure unique values in the array
|
||||
}
|
||||
constraint unique_c1 {
|
||||
unique {uniq_val_darr}; // Ensure unique values in the array
|
||||
unique {uniq_val_hash}; // Ensure unique values in the array
|
||||
unique {uniq_val_queue}; // Ensure unique values in the array
|
||||
unique {uniq_val_arr_mda}; // Ensure unique values in the array
|
||||
unique { array[0], array[1] };
|
||||
}
|
||||
// --------------------------------------------------
|
||||
// Explicit uniqueness checker (post-solve validation)
|
||||
// --------------------------------------------------
|
||||
function bit check_unique();
|
||||
for (int i = 0; i < $size(uniq_val_arr); i++) begin
|
||||
for (int j = i + 1; j < $size(uniq_val_arr); j++) begin
|
||||
if (uniq_val_arr[i] == uniq_val_arr[j]) begin
|
||||
$error("UNIQUENESS VIOLATION: uniq_val_arr[%0d] == uniq_val_arr[%0d] == 0x%h", i, j, uniq_val_arr[i]);
|
||||
return 0;
|
||||
end
|
||||
end
|
||||
end
|
||||
return 1;
|
||||
endfunction
|
||||
|
||||
function void post_randomize();
|
||||
$display("Randomized values in uniq_val_arr: %p", uniq_val_arr);
|
||||
|
||||
if (!check_unique()) begin
|
||||
$fatal(1, "Post-randomize uniqueness check FAILED");
|
||||
end
|
||||
foreach (uniq_val_arr[i]) begin
|
||||
$display("uniq_val_arr[%0d] = 0x%h", i, uniq_val_arr[i]);
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass : UniqueMultipleArray
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
// Create an instance of the UniqueMultipleArray class
|
||||
UniqueMultipleArray array_instance = new();
|
||||
|
||||
// Attempt to randomize and verify the constraints
|
||||
/* verilator lint_off WIDTHTRUNC */
|
||||
assert (array_instance.randomize())
|
||||
else $error("Randomization failed!");
|
||||
end
|
||||
endmodule : t
|
||||
|
|
@ -4,10 +4,6 @@
|
|||
| ^~~~
|
||||
... 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_randomize.v:40:7: Constraint expression ignored (unsupported)
|
||||
: ... note: In instance 't'
|
||||
40 | unique { array[0], array[1] };
|
||||
| ^~~~~~
|
||||
%Warning-CONSTRAINTIGN: t/t_randomize.v:43:23: Constraint expression ignored (imperfect distribution)
|
||||
: ... note: In instance 't'
|
||||
43 | constraint order { solve length before header; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue