diff --git a/include/verilated_random.cpp b/include/verilated_random.cpp index 896b04381..7679020f6 100644 --- a/include/verilated_random.cpp +++ b/include/verilated_random.cpp @@ -808,7 +808,7 @@ bool VlRandomizer::next_check_only(VlRNGReseeds& rngr) { return nextRandomize(rn bool VlRandomizer::next(VlRNGReseeds& rngr) { return nextRandomize(rngr, false); } bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) { - if (!checkOnly && m_vars.empty() && m_unique_arrays.empty()) return true; + if (!checkOnly && m_vars.empty() && m_uniqueArrays.empty()) return true; if (checkOnly && m_vars.empty()) return true; // No rand members: trivially SAT VlSolverSession& sess = s_solverSession; const VerilatedLockGuard lock{sess.m_mutex}; @@ -843,21 +843,27 @@ bool VlRandomizer::nextRandomize(VlRNGReseeds& rngr, bool checkOnly) { std::vector VlRandomizer::buildUniqueExprs() const { std::vector exprs; - if (m_unique_arrays.empty()) return exprs; + if (m_uniqueArrays.empty()) return exprs; const auto arrVarsp = std::make_shared(m_arr_vars); - for (const std::string& baseName : m_unique_arrays) { - const auto it = m_vars.find(baseName); - if (it == m_vars.end()) continue; - const VlRandomVar& var = *it->second; - // Select the elements the array actually holds now, by their own index - // or key, rather than by ordinal position - var.setArrayInfo(arrVarsp); - // 'distinct' needs at least two operands; fewer elements are trivially unique - if (var.countMatchingElements(*arrVarsp, baseName) < 2) continue; + for (const auto& [key, arrNameVec] : m_uniqueArrays) { + uint32_t matchingElementsCount = 0; + std::ostringstream varsString; + + for (const auto& baseName : arrNameVec) { + const auto it = m_vars.find(baseName); + if (it == m_vars.end()) continue; + const VlRandomVar& var = *it->second; + // Select the elements the array actually holds now, by their own index + // or key, rather than by ordinal position + var.setArrayInfo(arrVarsp); + // 'distinct' needs at least two operands; fewer elements are trivially unique + matchingElementsCount += var.countMatchingElements(*arrVarsp, baseName); + var.emitGetValue(varsString); + } + + if (matchingElementsCount < 2) continue; std::ostringstream os; - os << "(__Vbv (distinct "; - var.emitGetValue(os); - os << "))"; + os << "(__Vbv (distinct " << varsString.str() << "))"; exprs.push_back(os.str()); } return exprs; @@ -1606,7 +1612,7 @@ void VlRandomizer::clearConstraints() { m_constraints_line.clear(); m_solveBefore.clear(); m_softConstraints.clear(); - m_unique_arrays.clear(); // Re-registered by constraint setup + m_uniqueArrays.clear(); // Re-registered by constraint setup // Keep m_vars for class member randomization } diff --git a/include/verilated_random.h b/include/verilated_random.h index ad5ad131f..730fbf24f 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -264,7 +264,8 @@ class VlRandomizer VL_NOT_FINAL { std::set m_disabledVars; // Variables with rand_mode off (skip write-back) // variables ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration - std::vector m_unique_arrays; // Arrays whose elements must be distinct + // Arrays whose elements must be distinct grouped by unique constraint + std::map> m_uniqueArrays; const VlQueue* m_randmodep = nullptr; // rand_mode state; const VlQueue* m_static_randmodep = nullptr; // Static rand_mode state (shared) std::unordered_set m_staticVars; // Names of static rand vars @@ -599,7 +600,9 @@ public: // This is the "Sender" API for the generated code. // The elements to make distinct are taken from the array element table at // solve time, so a container resized by the solver is handled correctly. - void rand_unique(const std::string& name) { m_unique_arrays.push_back(name); } + void rand_unique(const std::string& name, const uint32_t key) { + m_uniqueArrays[key].insert(name); + } // Recursively record all elements in an unpacked array template diff --git a/src/V3AstAttr.h b/src/V3AstAttr.h index 4d5dcab2e..eb130ef9c 100644 --- a/src/V3AstAttr.h +++ b/src/V3AstAttr.h @@ -928,7 +928,7 @@ inline std::ostream& operator<<(std::ostream& os, const VBranchPred& rhs) { macro(RANDOMIZER_DISABLE_SOFT, "disable_soft", false, "r") \ macro(RANDOMIZER_HARD, "hard", false, "r+") \ macro(RANDOMIZER_SOFT, "soft", false, "rrrr") \ - macro(RANDOMIZER_UNIQUE, "rand_unique", false, "r") \ + macro(RANDOMIZER_UNIQUE, "rand_unique", false, "rr") \ macro(RANDOMIZER_MARK_RANDC, "markRandc", false, "r") \ macro(RANDOMIZER_SOLVE_BEFORE, "solveBefore", false, "rr") \ macro(RANDOMIZER_PIN_VAR, "pin_var", false, "rrr") \ diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index a2f9d2993..70d931543 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -786,6 +786,7 @@ class ConstraintExprVisitor final : public VNVisitor { std::set* m_sizeConstrainedArraysp = nullptr; // Arrays with size+element constraints AstNodeExpr* m_conditionp = nullptr; // Condition under which current expression is defined // (nullptr == always defined) + uint32_t* m_uniqueConstraintId = nullptr; // Current ID of unique call AstNode* m_firstExpressionInsideIndexp = nullptr; class NestedAccessPath final { @@ -2738,9 +2739,11 @@ class ConstraintExprVisitor final : public VNVisitor { = new AstCMethodHard{fl, new AstVarRef{fl, genModp, genVarp, VAccess::READ}, VCMethod::RANDOMIZER_UNIQUE, namep}; randUniqueCallp->dtypep(nodep->findVoidDType()); + randUniqueCallp->addPinsp(new AstConst{fl, *m_uniqueConstraintId}); setupStmtsp = AstNode::addNext(setupStmtsp, new AstStmtExpr{fl, randUniqueCallp}); } } + ++(*m_uniqueConstraintId); if (m_wantSingle && !smtExprs.empty()) { std::string exprStr = smtExprs.front(); if (smtExprs.size() > 1) { @@ -3338,6 +3341,7 @@ public: explicit ConstraintExprVisitor(AstClass* classp, VMemberMap& memberMap, AstNode* nodep, AstNodeFTask* inlineInitTaskp, AstVar* genp, AstVar* randModeVarp, std::set& writtenVars, + uint32_t* uniqueConstraintId, AstNodeFTask* memberselInitTaskp = nullptr, std::set* sizeConstrainedArraysp = nullptr) : m_classp{classp} @@ -3347,7 +3351,8 @@ public: , m_randModeVarp{randModeVarp} , m_memberMap{memberMap} , m_writtenVars{writtenVars} - , m_sizeConstrainedArraysp{sizeConstrainedArraysp} { + , m_sizeConstrainedArraysp{sizeConstrainedArraysp} + , m_uniqueConstraintId{uniqueConstraintId} { // Pre-pass before SMT lowering: extract conditional disable-soft // directives as runtime AstIf statements and append them to the // constraint-items chain so they reach the setup task body. The SMT @@ -3668,6 +3673,7 @@ class RandomizeVisitor final : public VNVisitor { std::map m_staticRandModeVars; // Static rand mode vars per class std::map> m_prePostWrap; // Per-handle-type pre/post virtual wrapper presence + uint32_t m_uniqueConstraintId = 0; // current ID of unique call // METHODS // Check if two nodes are semantically equivalent (not pointer equality): @@ -5736,9 +5742,9 @@ class RandomizeVisitor final : public VNVisitor { lowerDistConstraints(taskp, constrp->itemsp(), randModeVarp); } std::set& sizeArrays = m_sizeConstrainedArrays[classp]; - ConstraintExprVisitor{classp, m_memberMap, constrp->itemsp(), - nullptr, genp, randModeVarp, - m_writtenVars, randomizep, &sizeArrays}; + ConstraintExprVisitor{ + classp, m_memberMap, constrp->itemsp(), nullptr, genp, + randModeVarp, m_writtenVars, &m_uniqueConstraintId, randomizep, &sizeArrays}; if (constrp->itemsp()) { taskp->addStmtsp(wrapIfConstraintMode( nodep, constrp, constrp->itemsp()->unlinkFrBackWithNext())); @@ -6219,8 +6225,9 @@ class RandomizeVisitor final : public VNVisitor { randomizeFuncp->addStmtsp(capturedTreep); { expandUniqueElementList(capturedTreep); - ConstraintExprVisitor{nullptr, m_memberMap, capturedTreep, randomizeFuncp, - stdrand, nullptr, m_writtenVars, nullptr}; + ConstraintExprVisitor{ + nullptr, m_memberMap, capturedTreep, randomizeFuncp, stdrand, + nullptr, m_writtenVars, &m_uniqueConstraintId, nullptr}; } AstCExpr* const solverCallp = new AstCExpr{fl}; solverCallp->dtypeSetBit(); @@ -6437,7 +6444,8 @@ class RandomizeVisitor final : public VNVisitor { { expandUniqueElementList(capturedTreep); ConstraintExprVisitor{classp, m_memberMap, capturedTreep, randomizeFuncp, - localGenp, randModeVarp, m_writtenVars, nullptr}; + localGenp, randModeVarp, m_writtenVars, &m_uniqueConstraintId, + nullptr}; } // Call the solver and set return value diff --git a/test_regress/t/t_constraint_unq_arr.v b/test_regress/t/t_constraint_unq_arr.v index 89706b351..415a74d2f 100644 --- a/test_regress/t/t_constraint_unq_arr.v +++ b/test_regress/t/t_constraint_unq_arr.v @@ -19,13 +19,12 @@ class C extends Subclass; sub_arr = new[10]; endfunction - function bit check_unique(); + function void check_unique(); // dynamic array inside class for (int i = 0; i < $size(arr); i++) begin for (int j = i + 1; j < $size(arr); j++) begin if (arr[i] == arr[j]) begin $error("UNIQUENESS VIOLATION: arr[%0d] == arr[%0d] == 0x%h", i, j, arr[i]); - return 0; end end end @@ -34,11 +33,9 @@ class C extends Subclass; for (int j = i + 1; j < $size(sub_arr); j++) begin if (sub_arr[i] == sub_arr[j]) begin $error("UNIQUENESS VIOLATION: arr[%0d] == arr[%0d] == 0x%h", i, j, arr[i]); - return 0; end end end - return 1; endfunction constraint c { @@ -54,6 +51,7 @@ module t; rand bit [15:0] queue[$]; rand bit [15:0] queue_c[$:3]; rand bit [15:0] assoc[int]; + rand bit [3:0] multiarr0[4], multiarr1[4]; constraint c { unique {arr}; @@ -61,6 +59,7 @@ module t; unique {queue}; unique {queue_c}; unique {assoc}; + unique {multiarr0, multiarr1}; } function new; @@ -70,13 +69,12 @@ module t; assoc = '{0: 0, 1: 0, 3: 0, 5: 0}; endfunction - function bit check_unique(); + function void check_unique(); // static array for (int i = 0; i < $size(arr); i++) begin for (int j = i + 1; j < $size(arr); j++) begin if (arr[i] == arr[j]) begin $error("UNIQUENESS VIOLATION: arr[%0d] == arr[%0d] == 0x%h", i, j, arr[i]); - return 0; end end end @@ -85,7 +83,6 @@ module t; for (int j = i + 1; j < darr.size(); j++) begin if (darr[i] == darr[j]) begin $error("UNIQUENESS VIOLATION: darr[%0d] == darr[%0d] == 0x%h", i, j, darr[i]); - return 0; end end end @@ -94,7 +91,6 @@ module t; for (int j = i + 1; j < queue.size(); j++) begin if (queue[i] == queue[j]) begin $error("UNIQUENESS VIOLATION: queue[%0d] == queue[%0d] == 0x%h", i, j, queue[i]); - return 0; end end end @@ -103,7 +99,6 @@ module t; for (int j = i + 1; j < queue_c.size(); j++) begin if (queue_c[i] == queue_c[j]) begin $error("UNIQUENESS VIOLATION: queue_c[%0d] == queue_c[%0d] == 0x%h", i, j, queue_c[i]); - return 0; end end end @@ -115,11 +110,37 @@ module t; end if (assoc[i] == assoc[j]) begin $error("UNIQUENESS VIOLATION: assoc[%0d] == assoc[%0d] == 0x%h", i, j, assoc[i]); - return 0; end end end - return 1; + // multiple arrays + foreach (multiarr0[i]) begin + foreach (multiarr0[j]) begin + if (i == j) begin + continue; + end + if (multiarr0[i] == multiarr0[j]) begin + $error("UNIQUENESS VIOLATION: multiarr0[%0d] == multiarr0[%0d] == 0x%h", i, j, multiarr0[i]); + end + end + end + foreach (multiarr1[i]) begin + foreach (multiarr1[j]) begin + if (i == j) begin + continue; + end + if (multiarr1[i] == multiarr1[j]) begin + $error("UNIQUENESS VIOLATION: multiarr1[%0d] == multiarr1[%0d] == 0x%h", i, j, multiarr1[i]); + end + end + end + foreach (multiarr1[i]) begin + foreach (multiarr0[j]) begin + if (multiarr0[i] == multiarr1[j]) begin + $error("UNIQUENESS VIOLATION: multiarr0[%0d] == multiarr1[%0d] == 0x%h", i, j, multiarr0[i]); + end + end + end endfunction endclass : UniqueMultipleArray @@ -131,8 +152,8 @@ module t; cc.randomize(); a.randomize(); - assert(cc.check_unique()); - assert(a.check_unique()); + repeat(20) cc.check_unique(); + repeat(20) a.check_unique(); $write("*-* All Finished *-*\n"); $finish;