Support parsing and otherwise ignoring inline constraints (#5126)

This commit is contained in:
Arkadiusz Kozdra 2024-06-13 14:38:20 +02:00 committed by GitHub
parent 3203019408
commit 442c9bc316
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 193 additions and 29 deletions

View File

@ -2260,7 +2260,7 @@ class AstWith final : public AstNodeExpr {
// @astgen op3 := exprp : List[AstNode] // @astgen op3 := exprp : List[AstNode]
public: public:
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp, AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
AstNodeExpr* exprp) AstNode* exprp)
: ASTGEN_SUPER_With(fl) { : ASTGEN_SUPER_With(fl) {
this->indexArgRefp(indexArgRefp); this->indexArgRefp(indexArgRefp);
this->valueArgRefp(valueArgRefp); this->valueArgRefp(valueArgRefp);
@ -2283,13 +2283,13 @@ class AstWithParse final : public AstNodeExpr {
// Replaced with AstWith // Replaced with AstWith
// Parents: expr|stmt // Parents: expr|stmt
// Children: funcref, expr // Children: funcref, expr
// @astgen op1 := funcrefp : AstNode // @astgen op1 := funcrefp : AstNodeExpr
// @astgen op2 := exprp : Optional[AstNodeExpr] // @astgen op2 := exprsp : List[AstNode]
public: public:
AstWithParse(FileLine* fl, AstNode* funcrefp, AstNodeExpr* exprp) AstWithParse(FileLine* fl, AstNodeExpr* funcrefp, AstNode* exprsp)
: ASTGEN_SUPER_WithParse(fl) { : ASTGEN_SUPER_WithParse(fl) {
this->funcrefp(funcrefp); this->funcrefp(funcrefp);
this->exprp(exprp); this->addExprsp(exprsp);
} }
ASTGEN_MEMBERS_AstWithParse; ASTGEN_MEMBERS_AstWithParse;
bool same(const AstNode* /*samep*/) const override { return true; } bool same(const AstNode* /*samep*/) const override { return true; }

View File

@ -1521,11 +1521,11 @@ class LinkDotFindVisitor final : public VNVisitor {
VL_DO_DANGLING(argp->unlinkFrBackWithNext()->deleteTree(), argp); VL_DO_DANGLING(argp->unlinkFrBackWithNext()->deleteTree(), argp);
} }
// Type depends on the method used, let V3Width figure it out later // Type depends on the method used, let V3Width figure it out later
if (nodep->exprp()) { // Else empty expression and pretend no "with" if (nodep->exprsp()) { // Else empty expression and pretend no "with"
const auto indexArgRefp = new AstLambdaArgRef{argFl, name + "__DOT__index", true}; const auto indexArgRefp = new AstLambdaArgRef{argFl, name + "__DOT__index", true};
const auto valueArgRefp = new AstLambdaArgRef{argFl, name, false}; const auto valueArgRefp = new AstLambdaArgRef{argFl, name, false};
const auto newp = new AstWith{nodep->fileline(), indexArgRefp, valueArgRefp, const auto newp = new AstWith{nodep->fileline(), indexArgRefp, valueArgRefp,
nodep->exprp()->unlinkFrBackWithNext()}; nodep->exprsp()->unlinkFrBackWithNext()};
funcrefp->addPinsp(newp); funcrefp->addPinsp(newp);
} }
nodep->replaceWith(funcrefp->unlinkFrBack()); nodep->replaceWith(funcrefp->unlinkFrBack());
@ -1545,6 +1545,7 @@ class LinkDotFindVisitor final : public VNVisitor {
// Insert argref's name into symbol table // Insert argref's name into symbol table
m_statep->insertSym(m_curSymp, nodep->valueArgRefp()->name(), nodep->valueArgRefp(), m_statep->insertSym(m_curSymp, nodep->valueArgRefp()->name(), nodep->valueArgRefp(),
nullptr); nullptr);
iterateChildren(nodep);
} }
} }
@ -2213,16 +2214,21 @@ class LinkDotResolveVisitor final : public VNVisitor {
return reinterpret_cast<VSymEnt*>(clockingp->eventp()->user1p()); return reinterpret_cast<VSymEnt*>(clockingp->eventp()->user1p());
} }
bool isParamedClassRefDType(const AstNode* classp) {
while (const AstRefDType* const refp = VN_CAST(classp, RefDType))
classp = refp->subDTypep();
return (VN_IS(classp, ClassRefDType) && VN_AS(classp, ClassRefDType)->paramsp())
|| VN_IS(classp, ParamTypeDType);
}
bool isParamedClassRef(const AstNode* nodep) { bool isParamedClassRef(const AstNode* nodep) {
// Is this a parameterized reference to a class, or a reference to class parameter // Is this a parameterized reference to a class, or a reference to class parameter
if (const auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) { if (const auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
if (classRefp->paramsp()) return true; if (classRefp->paramsp()) return true;
const auto* classp = classRefp->classOrPackageNodep(); const auto* classp = classRefp->classOrPackageNodep();
while (const auto* typedefp = VN_CAST(classp, Typedef)) classp = typedefp->subDTypep(); while (const auto* typedefp = VN_CAST(classp, Typedef)) classp = typedefp->subDTypep();
return (VN_IS(classp, ClassRefDType) && VN_AS(classp, ClassRefDType)->paramsp()) return isParamedClassRefDType(classp);
|| VN_IS(classp, ParamTypeDType);
} }
return false; return isParamedClassRefDType(nodep);
} }
VSymEnt* getThisClassSymp() { VSymEnt* getThisClassSymp() {
VSymEnt* classSymp = m_ds.m_dotSymp; VSymEnt* classSymp = m_ds.m_dotSymp;
@ -2637,7 +2643,16 @@ class LinkDotResolveVisitor final : public VNVisitor {
= VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef); = VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
classOrPackagep = cpackagerefp->classOrPackagep(); classOrPackagep = cpackagerefp->classOrPackagep();
UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link"); UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link");
if (cpackagerefp->name() == "local::") {
if (m_pinSymp) {
m_ds.m_dotSymp = m_curSymp->fallbackp();
} else {
nodep->v3error("Illegal 'local::' outside 'randomize() with'");
m_ds.m_dotErr = true;
}
} else {
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep); m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
}
m_ds.m_dotPos = DP_SCOPE; m_ds.m_dotPos = DP_SCOPE;
} else if (m_ds.m_dotPos == DP_SCOPE) { } else if (m_ds.m_dotPos == DP_SCOPE) {
// {a}.{b}, where {a} maybe a module name // {a}.{b}, where {a} maybe a module name
@ -3096,8 +3111,42 @@ class LinkDotResolveVisitor final : public VNVisitor {
void visit(AstMethodCall* nodep) override { void visit(AstMethodCall* nodep) override {
// Created here so should already be resolved. // Created here so should already be resolved.
VL_RESTORER(m_ds); VL_RESTORER(m_ds);
VL_RESTORER(m_pinSymp);
{ {
m_ds.init(m_curSymp); m_ds.init(m_curSymp);
if (nodep->name() == "randomize" && VN_IS(nodep->pinsp(), With)) {
const AstNodeDType* fromDtp = nodep->fromp()->dtypep();
if (!fromDtp) {
if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) {
fromDtp = varRefp->varp()->subDTypep();
} else if (const AstNodeSel* const selp = VN_CAST(nodep->fromp(), NodeSel)) {
if (const AstNodeVarRef* const varRefp
= VN_CAST(selp->fromp(), NodeVarRef)) {
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
}
} else if (const AstNodePreSel* const selp
= VN_CAST(nodep->fromp(), NodePreSel)) {
if (const AstNodeVarRef* const varRefp
= VN_CAST(selp->fromp(), NodeVarRef)) {
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
}
}
if (!fromDtp)
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: 'randomize() with' on complex expressions");
}
if (m_statep->forPrimary() && isParamedClassRefDType(fromDtp)) {
m_ds.m_unresolvedClass = true;
} else if (fromDtp) {
const AstClassRefDType* const classDtp
= VN_CAST(fromDtp->skipRefp(), ClassRefDType);
if (!classDtp)
nodep->v3error("'randomize() with' on a non-class-instance "
<< fromDtp->prettyNameQ());
else
m_pinSymp = m_statep->getNodeSym(classDtp->classp());
}
}
iterateChildren(nodep); iterateChildren(nodep);
} }
} }
@ -3113,7 +3162,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
if (nodep->user3SetOnce()) return; if (nodep->user3SetOnce()) return;
UINFO(8, " " << nodep << endl); UINFO(8, " " << nodep << endl);
UINFO(8, " " << m_ds.ascii() << endl); UINFO(8, " " << m_ds.ascii() << endl);
{ if (m_ds.m_dotPos != DP_MEMBER || nodep->name() != "randomize") {
// Visit arguments at the beginning. // Visit arguments at the beginning.
// They may be visitted even if the current node can't be linked now. // They may be visitted even if the current node can't be linked now.
VL_RESTORER(m_ds); VL_RESTORER(m_ds);
@ -3457,6 +3506,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
VL_RESTORER(m_curSymp); VL_RESTORER(m_curSymp);
{ {
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep); m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
if (m_pinSymp) m_curSymp->importFromClass(m_statep->symsp(), m_pinSymp);
iterateChildren(nodep); iterateChildren(nodep);
} }
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp); m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);

View File

@ -3646,10 +3646,17 @@ class WidthVisitor final : public VNVisitor {
void methodCallClass(AstMethodCall* nodep, AstClassRefDType* adtypep) { void methodCallClass(AstMethodCall* nodep, AstClassRefDType* adtypep) {
// No need to width-resolve the class, as it was done when we did the child // No need to width-resolve the class, as it was done when we did the child
AstClass* const first_classp = adtypep->classp(); AstClass* const first_classp = adtypep->classp();
AstWith* withp = nullptr;
if (nodep->name() == "randomize") { if (nodep->name() == "randomize") {
withp = methodWithArgument(nodep, false, false, adtypep->findVoidDType(),
adtypep->findBitDType(), adtypep);
methodOkArguments(nodep, 0, 0);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
V3Randomize::newRandomizeFunc(first_classp); V3Randomize::newRandomizeFunc(first_classp);
m_memberMap.clear(); m_memberMap.clear();
} else if (nodep->name() == "srandom") { } else if (nodep->name() == "srandom") {
methodOkArguments(nodep, 1, 1);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
V3Randomize::newSRandomFunc(first_classp); V3Randomize::newSRandomFunc(first_classp);
m_memberMap.clear(); m_memberMap.clear();
} }
@ -3695,6 +3702,10 @@ class WidthVisitor final : public VNVisitor {
nodep->classOrPackagep(classp); nodep->classOrPackagep(classp);
if (VN_IS(ftaskp, Task)) nodep->dtypeSetVoid(); if (VN_IS(ftaskp, Task)) nodep->dtypeSetVoid();
processFTaskRefArgs(nodep); processFTaskRefArgs(nodep);
if (withp) {
withp->v3warn(CONSTRAINTIGN, "'with' constraint ignored (unsupported)");
VL_DO_DANGLING(withp->deleteTree(), withp);
}
} }
return; return;
} else if (nodep->name() == "get_randstate" || nodep->name() == "set_randstate") { } else if (nodep->name() == "get_randstate" || nodep->name() == "set_randstate") {
@ -5993,16 +6004,25 @@ class WidthVisitor final : public VNVisitor {
m_withp = nodep; m_withp = nodep;
userIterateChildren(nodep->indexArgRefp(), nullptr); userIterateChildren(nodep->indexArgRefp(), nullptr);
userIterateChildren(nodep->valueArgRefp(), nullptr); userIterateChildren(nodep->valueArgRefp(), nullptr);
if (!nodep->exprp()->hasDType()) {
userIterateAndNext(nodep->exprp(), nullptr);
} else {
if (vdtypep) { if (vdtypep) {
userIterateAndNext(nodep->exprp(), WidthVP{nodep->dtypep(), PRELIM}.p()); userIterateAndNext(nodep->exprp(), WidthVP{nodep->dtypep(), PRELIM}.p());
} else { // 'sort with' allows arbitrary type } else { // 'sort with' allows arbitrary type
userIterateAndNext(nodep->exprp(), WidthVP{SELF, PRELIM}.p()); userIterateAndNext(nodep->exprp(), WidthVP{SELF, PRELIM}.p());
} }
}
if (!nodep->exprp()->hasDType()) {
nodep->dtypeSetVoid();
} else {
nodep->dtypeFrom(nodep->exprp()); nodep->dtypeFrom(nodep->exprp());
iterateCheckAssign(nodep, "'with' return value", nodep->exprp(), FINAL, iterateCheckAssign(nodep, "'with' return value", nodep->exprp(), FINAL,
nodep->dtypep()); nodep->dtypep());
} }
} }
}
void visit(AstLambdaArgRef* nodep) override { void visit(AstLambdaArgRef* nodep) override {
UASSERT_OBJ(m_withp, nodep, "LambdaArgRef not underneath 'with' lambda"); UASSERT_OBJ(m_withp, nodep, "LambdaArgRef not underneath 'with' lambda");
if (nodep->index()) { if (nodep->index()) {

View File

@ -4111,8 +4111,7 @@ function_subroutine_callNoMethod<nodeExprp>: // IEEE: function_subroutine
// // IEEE: randomize_call // // IEEE: randomize_call
// // We implement randomize as a normal funcRef, since randomize isn't a keyword // // We implement randomize as a normal funcRef, since randomize isn't a keyword
// // Note yNULL is already part of expressions, so they come for free // // Note yNULL is already part of expressions, so they come for free
| funcRef yWITH__CUR constraint_block | funcRef yWITH__CUR constraint_block { $$ = new AstWithParse{$2, $1, $3}; }
{ $$ = $1; BBUNSUP($2, "Unsupported: randomize() 'with' constraint"); }
| funcRef yWITH__CUR '{' '}' { $$ = new AstWithParse{$2, $1, nullptr}; } | funcRef yWITH__CUR '{' '}' { $$ = new AstWithParse{$2, $1, nullptr}; }
; ;
@ -7198,8 +7197,7 @@ localNextId<nodeExprp>: // local
// // Must call nextId without any additional tokens following // // Must call nextId without any additional tokens following
yLOCAL__COLONCOLON yLOCAL__COLONCOLON
{ $$ = new AstClassOrPackageRef{$1, "local::", PARSEP->unitPackage($<fl>1), nullptr}; { $$ = new AstClassOrPackageRef{$1, "local::", PARSEP->unitPackage($<fl>1), nullptr};
SYMP->nextId(PARSEP->rootp()); SYMP->nextId(PARSEP->rootp()); }
BBUNSUP($<fl>1, "Unsupported: Randomize 'local::'"); }
; ;
//^^^========= //^^^=========

View File

@ -0,0 +1,7 @@
%Error: t/t_package_local_bad.v:9:23: Illegal 'local::' outside 'randomize() with'
9 | $display(local::x);
| ^
%Error: t/t_package_local_bad.v:9:23: Can't find definition of scope/variable/func: 'x'
9 | $display(local::x);
| ^
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2019 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
scenarios(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,12 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2024 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
module t(/*AUTOARG*/);
initial begin
$display(local::x);
$finish;
end
endmodule

View File

@ -1,9 +1,12 @@
%Warning-CONSTRAINTIGN: t/t_randomize.v:66:25: 'with' constraint ignored (unsupported)
66 | v = p.randomize() with { if_4 == local::if_4; header == 2; };
| ^~~~
... 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:38:16: State-dependent constraint ignored (unsupported) %Warning-CONSTRAINTIGN: t/t_randomize.v:38:16: State-dependent constraint ignored (unsupported)
: ... note: In instance 't' : ... note: In instance 't'
38 | array[i] inside {2, 4, 6}; 38 | array[i] inside {2, 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:26:7: Constraint expression ignored (unsupported) %Warning-CONSTRAINTIGN: t/t_randomize.v:26:7: Constraint expression ignored (unsupported)
: ... note: In instance 't' : ... note: In instance 't'
26 | if (header > 4) { 26 | if (header > 4) {

View File

@ -57,11 +57,14 @@ module t (/*AUTOARG*/);
initial begin initial begin
int v; int v;
bit if_4 = '0;
// TODO not testing constrained values // TODO not testing constrained values
v = p.randomize(); v = p.randomize();
if (v != 1) $stop; if (v != 1) $stop;
v = p.randomize() with {}; v = p.randomize() with {};
if (v != 1) $stop; if (v != 1) $stop;
v = p.randomize() with { if_4 == local::if_4; header == 2; };
if (v != 1) $stop;
// TODO not testing other randomize forms as unused in UVM // TODO not testing other randomize forms as unused in UVM
$write("*-* All Finished *-*\n"); $write("*-* All Finished *-*\n");

View File

@ -0,0 +1,14 @@
%Error-UNSUPPORTED: t/t_randomize_method_complex_bad.v:16:11: Unsupported: 'randomize() with' on complex expressions
16 | x.f.randomize() with { r < 5; },
| ^~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: t/t_randomize_method_complex_bad.v:16:30: Can't find definition of variable: 'r'
16 | x.f.randomize() with { r < 5; },
| ^
%Error: t/t_randomize_method_complex_bad.v:17:9: 'randomize() with' on a non-class-instance 'int'
17 | i.randomize() with { v < 5; });
| ^~~~~~~~~
%Error: t/t_randomize_method_complex_bad.v:17:28: Can't find definition of variable: 'v'
17 | i.randomize() with { v < 5; });
| ^
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2020 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
scenarios(linter => 1);
lint(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,18 @@
// 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
class Cls;
Cls f;
rand int r;
endclass
module t (/*AUTOARG*/);
Cls x = new;
int i;
initial $display(
x.f.randomize(),
x.f.randomize() with { r < 5; },
i.randomize() with { v < 5; });
endmodule

View File

@ -1,11 +1,12 @@
%Error-UNSUPPORTED: t/t_randomize_method_with_unsup.v:47:40: Unsupported: randomize() 'with' constraint %Warning-CONSTRAINTIGN: t/t_randomize_method_with_unsup.v:47:40: 'with' constraint ignored (unsupported)
47 | rand_result = obj.randomize() with { lb <= y && y <= ub; }; 47 | rand_result = obj.randomize() with { lb <= y && y <= ub; };
| ^~~~ | ^~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest
%Error-UNSUPPORTED: t/t_randomize_method_with_unsup.v:63:37: Unsupported: randomize() 'with' constraint ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message.
%Warning-CONSTRAINTIGN: t/t_randomize_method_with_unsup.v:63:37: 'with' constraint ignored (unsupported)
63 | rand_result = obj.randomize() with { 256 < y && y < 256; }; 63 | rand_result = obj.randomize() with { 256 < y && y < 256; };
| ^~~~ | ^~~~
%Error-UNSUPPORTED: t/t_randomize_method_with_unsup.v:67:37: Unsupported: randomize() 'with' constraint %Warning-CONSTRAINTIGN: t/t_randomize_method_with_unsup.v:67:37: 'with' constraint ignored (unsupported)
67 | rand_result = obj.randomize() with { 16 <= z && z <= 32; }; 67 | rand_result = obj.randomize() with { 16 <= z && z <= 32; };
| ^~~~ | ^~~~
%Error: Exiting due to %Error: Exiting due to