From 5470cf9fa92caafc5be8952472cbd24f8b050bd9 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Fri, 15 Nov 2024 16:45:06 +0100 Subject: [PATCH] Support randomize size constraints with restrictions (#5582 partial) (#5611) --- include/verilated_types.h | 1 + src/V3AstNodes.cpp | 1 + src/V3Randomize.cpp | 97 ++++++++++++++++++- .../t/t_randomize_method_types_unsup.out | 17 +++- .../t/t_randomize_method_types_unsup.v | 14 ++- test_regress/t/t_randomize_queue_size.py | 21 ++++ test_regress/t/t_randomize_queue_size.v | 85 ++++++++++++++++ test_regress/t/uvm/uvm_pkg_todo.svh | 8 +- 8 files changed, 229 insertions(+), 15 deletions(-) create mode 100755 test_regress/t/t_randomize_queue_size.py create mode 100755 test_regress/t/t_randomize_queue_size.v diff --git a/include/verilated_types.h b/include/verilated_types.h index 773b20212..2e8af51ee 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -570,6 +570,7 @@ public: m_deque.resize(size, atDefault()); } } + void resize(size_t size) { m_deque.resize(size, atDefault()); } // function void q.push_front(value) void push_front(const T_Value& value) { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 54987e853..fbb5eb3de 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2903,6 +2903,7 @@ void AstCMethodHard::setPurity() { {"r_xor", true}, {"renew", false}, {"renew_copy", false}, + {"resize", false}, {"resume", false}, {"reverse", false}, {"rsort", false}, diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 9b059d512..0390e3cbc 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -606,6 +606,12 @@ class ConstraintExprVisitor final : public VNVisitor { // VISITORS void visit(AstNodeVarRef* nodep) override { AstVar* const varp = nodep->varp(); + if (varp->user4p()) { + varp->user4p()->v3warn( + CONSTRAINTIGN, + "Size constraint combined with element constraint may not work correctly"); + } + AstNodeModule* const classOrPackagep = nodep->classOrPackagep(); const RandomizeMode randMode = {.asInt = varp->user1()}; if (!randMode.usesMode && editFormat(nodep)) return; @@ -1132,7 +1138,9 @@ class RandomizeVisitor final : public VNVisitor { // AstClass::user2p() -> AstVar*. Rand mode state variable // AstVar::user3() -> bool. Handled in constraints // AstClass::user3p() -> AstVar*. Constrained randomizer variable + // AstConstraint::user3p() -> AstTask*. Pointer to resize procedure // AstClass::user4p() -> AstVar*. Constraint mode state variable + // AstVar::user4p() -> AstVar*. Size variable for constrained queues // VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor) // VNUser2InUse m_inuser2; (Allocated for use in RandomizeMarkVisitor) const VNUser3InUse m_inuser3; @@ -1150,6 +1158,7 @@ class RandomizeVisitor final : public VNVisitor { size_t m_enumValueTabCount = 0; // Number of tables with enum values created int m_randCaseNum = 0; // Randcase number within a module for var naming std::map m_randcDtypes; // RandC data type deduplication + AstConstraint* m_constraintp = nullptr; // Current constraint // METHODS void createRandomGenerator(AstClass* const classp) { @@ -1180,6 +1189,17 @@ class RandomizeVisitor final : public VNVisitor { m_memberMap.insert(classp, setupAllTaskp); return setupAllTaskp; } + AstTask* getCreateAggrResizeTask(AstClass* const classp) { + static const char* const name = "__Vresize_constrained_arrays"; + AstTask* resizeTaskp = VN_AS(m_memberMap.findMember(classp, name), Task); + if (resizeTaskp) return resizeTaskp; + resizeTaskp = new AstTask{classp->fileline(), name, nullptr}; + resizeTaskp->classMethod(true); + resizeTaskp->isVirtual(true); + classp->addMembersp(resizeTaskp); + m_memberMap.insert(classp, resizeTaskp); + return resizeTaskp; + } AstVar* getCreateRandModeVar(AstClass* const classp) { if (classp->user2p()) return VN_AS(classp->user2p(), Var); if (AstClassExtends* const extendsp = classp->extendsp()) { @@ -1281,8 +1301,7 @@ class RandomizeVisitor final : public VNVisitor { FileLine* fl = modeVarp->fileline(); AstCMethodHard* const dynarrayNewp = new AstCMethodHard{fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE}, - "renew_copy", new AstConst{fl, modeCount}}; - dynarrayNewp->addPinsp(new AstVarRef{fl, modeVarModp, modeVarp, VAccess::READ}); + "resize", new AstConst{fl, modeCount}}; dynarrayNewp->dtypeSetVoid(); AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask); UASSERT_OBJ(newp, classp, "No new() in class"); @@ -1565,6 +1584,13 @@ class RandomizeVisitor final : public VNVisitor { nodep->addMembersp(taskp); return taskp; } + AstTask* newResizeConstrainedArrayTask(AstClass* const nodep, const std::string& name) { + AstTask* const taskp + = new AstTask{nodep->fileline(), name + "_resize_constrained_array", nullptr}; + taskp->classMethod(true); + nodep->addMembersp(taskp); + return taskp; + } AstNodeStmt* implementConstraintsClear(FileLine* const fileline, AstVar* const genp) { AstCMethodHard* const clearp = new AstCMethodHard{ fileline, @@ -1898,6 +1924,15 @@ class RandomizeVisitor final : public VNVisitor { setupAllTaskp->addStmtsp(setupTaskRefp->makeStmt()); + if (AstTask* const resizeTaskp = VN_CAST(constrp->user3p(), Task)) { + AstTask* const resizeAllTaskp = getCreateAggrResizeTask(nodep); + AstTaskRef* const resizeTaskRefp + = new AstTaskRef{constrp->fileline(), resizeTaskp->name(), nullptr}; + resizeTaskRefp->taskp(resizeTaskp); + resizeTaskRefp->classOrPackagep(classp); + resizeAllTaskp->addStmtsp(resizeTaskRefp->makeStmt()); + } + ConstraintExprVisitor{m_memberMap, constrp->itemsp(), nullptr, genp, randModeVarp}; if (constrp->itemsp()) { taskp->addStmtsp(wrapIfConstraintMode( @@ -1933,6 +1968,13 @@ class RandomizeVisitor final : public VNVisitor { AstVarRef* const fvarRefp = new AstVarRef{fl, fvarp, VAccess::WRITE}; randomizep->addStmtsp(new AstAssign{fl, fvarRefp, beginValp}); + if (AstTask* const resizeAllTaskp + = VN_AS(m_memberMap.findMember(nodep, "__Vresize_constrained_arrays"), Task)) { + AstTaskRef* const resizeTaskRefp = new AstTaskRef{fl, resizeAllTaskp->name(), nullptr}; + resizeTaskRefp->taskp(resizeAllTaskp); + randomizep->addStmtsp(resizeTaskRefp->makeStmt()); + } + AstFunc* const basicRandomizep = V3Randomize::newRandomizeFunc(m_memberMap, nodep, "__Vbasic_randomize"); addBasicRandomizeBody(basicRandomizep, nodep, randModeVarp); @@ -2161,6 +2203,57 @@ class RandomizeVisitor final : public VNVisitor { UINFO(9, "Added `%s` randomization procedure"); VL_DO_DANGLING(withp->deleteTree(), withp); } + void visit(AstConstraint* nodep) override { + VL_RESTORER(m_constraintp); + m_constraintp = nodep; + iterateChildren(nodep); + } + void visit(AstCMethodHard* nodep) override { + iterateChildren(nodep); + FileLine* const fl = nodep->fileline(); + if (m_constraintp && nodep->fromp()->user1() && nodep->name() == "size") { + AstClass* const classp = VN_AS(m_modp, Class); + AstVarRef* const queueVarRefp = VN_CAST(nodep->fromp(), VarRef); + if (!queueVarRefp) { + // Warning from ConstraintExprVisitor will be thrown + return; + } + AstVar* const queueVarp = queueVarRefp->varp(); + AstVar* sizeVarp = VN_CAST(queueVarp->user4p(), Var); + if (!sizeVarp) { + sizeVarp = new AstVar{fl, VVarType::BLOCKTEMP, "__V" + queueVarp->name() + "_size", + nodep->findSigned32DType()}; + classp->addMembersp(sizeVarp); + m_memberMap.insert(classp, sizeVarp); + sizeVarp->user2p(classp); + + queueVarp->user4p(sizeVarp); + + AstTask* resizerTaskp = VN_AS(m_constraintp->user3p(), Task); + if (!resizerTaskp) { + resizerTaskp = newResizeConstrainedArrayTask(classp, m_constraintp->name()); + m_constraintp->user3p(resizerTaskp); + } + AstCMethodHard* const resizep + = new AstCMethodHard{fl, nodep->fromp()->unlinkFrBack(), "resize", + new AstVarRef{fl, sizeVarp, VAccess::READ}}; + resizep->dtypep(nodep->findVoidDType()); + resizerTaskp->addStmtsp(new AstStmtExpr{fl, resizep}); + + // Since size variable is signed int, we need additional constraint + // to make sure it is always >= 0. + AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ}; + sizeVarRefp->user1(true); + AstGteS* const sizeGtep = new AstGteS{fl, sizeVarRefp, new AstConst{fl, 0}}; + sizeGtep->user1(true); + m_constraintp->addItemsp(new AstConstraintExpr{fl, sizeGtep}); + } + AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ}; + sizeVarRefp->user1(true); + nodep->replaceWith(sizeVarRefp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + } + } void visit(AstNodeStmt* nodep) override { VL_RESTORER(m_stmtp); m_stmtp = nodep; diff --git a/test_regress/t/t_randomize_method_types_unsup.out b/test_regress/t/t_randomize_method_types_unsup.out index 2526171bc..4b53e95ef 100644 --- a/test_regress/t/t_randomize_method_types_unsup.out +++ b/test_regress/t/t_randomize_method_types_unsup.out @@ -1,10 +1,17 @@ -%Warning-CONSTRAINTIGN: t/t_randomize_method_types_unsup.v:13:32: Unsupported: randomizing this expression, treating as state - 13 | constraint dynsize { dynarr.size < 20; } - | ^~~~ +%Warning-CONSTRAINTIGN: t/t_randomize_method_types_unsup.v:17:17: Unsupported: randomizing this expression, treating as state + 17 | dynarr[1].size < 10; + | ^~~~ ... 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-UNSUPPORTED: t/t_randomize_method_types_unsup.v:10:13: Unsupported: random member variable with the type of the containing class +%Warning-CONSTRAINTIGN: t/t_randomize_method_types_unsup.v:21:9: Size constraint combined with element constraint may not work correctly + : ... note: In instance 't' + 21 | q.size < 5; + | ^~~~ +%Error-UNSUPPORTED: t/t_randomize_method_types_unsup.v:11:13: Unsupported: random member variable with the type of the containing class : ... note: In instance 't' - 10 | rand Cls cls; + 11 | rand Cls cls; | ^~~ +%Warning-CONSTRAINTIGN: t/t_randomize_method_types_unsup.v:33:43: Unsupported: randomizing this expression, treating as state + 33 | res = obj.randomize() with { dynarr.size > 2; }; + | ^~~~ %Error: Exiting due to diff --git a/test_regress/t/t_randomize_method_types_unsup.v b/test_regress/t/t_randomize_method_types_unsup.v index 8fbb96f52..b68abf141 100644 --- a/test_regress/t/t_randomize_method_types_unsup.v +++ b/test_regress/t/t_randomize_method_types_unsup.v @@ -6,12 +6,21 @@ class Cls; rand int assocarr[string]; - rand int dynarr[]; + rand int dynarr[][]; + rand int q[$]; rand Cls cls; rand int i; int st; - constraint dynsize { dynarr.size < 20; } + constraint dynsize { + dynarr.size < 20; + dynarr.size > 0; + dynarr[1].size < 10; + } constraint statedep { i < st + 2; } + constraint q_size_elem { + q.size < 5; + q[i] < 10; + } endclass module t (/*AUTOARG*/); @@ -21,5 +30,6 @@ module t (/*AUTOARG*/); initial begin obj = new; res = obj.randomize(); + res = obj.randomize() with { dynarr.size > 2; }; end endmodule diff --git a/test_regress/t/t_randomize_queue_size.py b/test_regress/t/t_randomize_queue_size.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_randomize_queue_size.py @@ -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() diff --git a/test_regress/t/t_randomize_queue_size.v b/test_regress/t/t_randomize_queue_size.v new file mode 100755 index 000000000..f1429f642 --- /dev/null +++ b/test_regress/t/t_randomize_queue_size.v @@ -0,0 +1,85 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +`define check_rand(cl, field, cond) \ +begin \ + longint prev_result; \ + int ok = 0; \ + if (!bit'(cl.randomize())) $stop; \ + prev_result = longint'(field); \ + if (!(cond)) $stop; \ + repeat(9) begin \ + longint result; \ + if (!bit'(cl.randomize())) $stop; \ + result = longint'(field); \ + if (!(cond)) $stop; \ + if (result != prev_result) ok = 1; \ + prev_result = result; \ + end \ + if (ok != 1) $stop; \ +end + +class Foo; + rand int q[$]; + rand int q2[$][$]; + int x = 1; + constraint c { + q.size() == 15; + q2.size() == 10; + } +endclass + +class Bar; + rand int q[$]; + rand int min_size; + rand int q2[$]; + constraint c { + min_size > 2; + q.size() >= min_size; + q.size() < 10; + }; + constraint c2 { + q2.size() < 7; + } +endclass + +class Baz; + rand Foo foo_arr[]; + constraint c_foo { + foo_arr.size == 7; + } +endclass + +module t; + initial begin + Foo foo = new; + Bar bar = new; + Baz baz = new; + + void'(foo.randomize()); + if (foo.q.size() != 15) $stop; + if (foo.q2.size() != 10) $stop; + + `check_rand(bar, bar.q.size(), bar.q.size() > 2 && bar.q.size() < 10); + `check_rand(bar, bar.q2.size(), bar.q2.size() < 7); + + baz.foo_arr = new[4]; + for (int i = 0; i < 4; i++) baz.foo_arr[i] = new; + baz.foo_arr[2].x = 2; + void'(baz.randomize()); + + if (baz.foo_arr.size() != 7) $stop; + for (int i = 0; i < 4; i++) + if (baz.foo_arr[i] == null) $stop; + for (int i = 4; i < 7; i++) + if (baz.foo_arr[i] != null) $stop; + if (baz.foo_arr[2].x != 2) $stop; + `check_rand(baz, baz.foo_arr[1].q[5], 1'b1); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/uvm/uvm_pkg_todo.svh b/test_regress/t/uvm/uvm_pkg_todo.svh index 0ef277ddb..65c2d0b3d 100644 --- a/test_regress/t/uvm/uvm_pkg_todo.svh +++ b/test_regress/t/uvm/uvm_pkg_todo.svh @@ -22444,9 +22444,7 @@ class uvm_reg_item extends uvm_sequence_item; uvm_elem_kind_e element_kind; uvm_object element; rand uvm_access_e kind; - //TODO issue-5582 - Rand constraint with .size - //TODO %Warning-CONSTRAINTIGN: t/t_uvm_pkg_todo.vh:#:#: Unsupported: randomizing this expression, treating as state - /*rand*/ uvm_reg_data_t value[]; + rand uvm_reg_data_t value[]; constraint max_values { value.size() > 0 && value.size() < 1000; } rand uvm_reg_addr_t offset; uvm_status_e status; @@ -26866,9 +26864,7 @@ class uvm_reg_fifo extends uvm_reg; local uvm_reg_field value; local int m_set_cnt; local int unsigned m_size; - //TODO issue-5582 - Rand constraint with .size - //TODO %Warning-CONSTRAINTIGN: t/t_uvm_pkg_todo.vh:#:#: Unsupported: randomizing this expression, treating as state - /*rand*/ uvm_reg_data_t fifo[$]; + rand uvm_reg_data_t fifo[$]; constraint valid_fifo_size { fifo.size() <= m_size; }