Support randomize size constraints with restrictions (#5582 partial) (#5611)

This commit is contained in:
Ryszard Rozak 2024-11-15 16:45:06 +01:00 committed by GitHub
parent 9bde98e912
commit 5470cf9fa9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 229 additions and 15 deletions

View File

@ -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) {

View File

@ -2903,6 +2903,7 @@ void AstCMethodHard::setPurity() {
{"r_xor", true},
{"renew", false},
{"renew_copy", false},
{"resize", false},
{"resume", false},
{"reverse", false},
{"rsort", false},

View File

@ -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<std::string, AstCDType*> 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;

View File

@ -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

View File

@ -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

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,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

View File

@ -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;
}