Fix parsing of `with (...) {...}` but still unsupported

This commit is contained in:
Wilson Snyder 2025-09-20 19:59:31 -04:00
parent a4db488b02
commit af54a26b43
9 changed files with 114 additions and 21 deletions

View File

@ -2439,7 +2439,8 @@ class AstWith final : public AstNodeExpr {
// Children: expression (equation establishing the with)
// @astgen op1 := indexArgRefp : AstLambdaArgRef
// @astgen op2 := valueArgRefp : AstLambdaArgRef
// @astgen op3 := exprp : List[AstNode]
// @astgen op3 := exprp : List[AstNode] // Pins, expression and constraints
// TODO: Separate expression and constraints
public:
AstWith(FileLine* fl, AstLambdaArgRef* indexArgRefp, AstLambdaArgRef* valueArgRefp,
AstNode* exprp)
@ -2466,12 +2467,15 @@ class AstWithParse final : public AstNodeExpr {
// Parents: expr|stmt
// Children: funcref, expr
// @astgen op1 := funcrefp : AstNodeExpr
// @astgen op2 := exprsp : List[AstNode]
// @astgen op3 := exprsp : List[AstNode] // With's parenthesis part
// @astgen op4 := constraintsp : List[AstNode] // With's braces part
public:
AstWithParse(FileLine* fl, AstNodeExpr* funcrefp, AstNode* exprsp)
AstWithParse(FileLine* fl, AstNodeExpr* funcrefp, AstNode* exprsp,
AstNode* constraintsp = nullptr)
: ASTGEN_SUPER_WithParse(fl) {
this->funcrefp(funcrefp);
addExprsp(exprsp);
addConstraintsp(constraintsp);
}
ASTGEN_MEMBERS_AstWithParse;
bool sameNode(const AstNode* /*samep*/) const override { return true; }

View File

@ -1812,12 +1812,24 @@ class LinkDotFindVisitor final : public VNVisitor {
VL_DO_DANGLING(argp->deleteTree(), argp);
}
// Type depends on the method used, let V3Width figure it out later
if (nodep->exprsp()) { // Else empty expression and pretend no "with"
if (nodep->exprsp()
|| nodep->constraintsp()) { // Else empty expression and pretend no "with"
AstNode* exprOrConstraintsp = nullptr;
if (nodep->exprsp() && nodep->constraintsp()) {
// When support this probably should change AstWith to separate out
// the expr from the constraint equation using separate op2/op3 similar
// to AstWithParse
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'randomize with (...) {...}'");
} else if (nodep->exprsp())
exprOrConstraintsp = nodep->exprsp()->unlinkFrBackWithNext();
if (nodep->constraintsp())
exprOrConstraintsp = AstNode::addNext(
exprOrConstraintsp, nodep->constraintsp()->unlinkFrBackWithNext());
AstLambdaArgRef* const indexArgRefp
= new AstLambdaArgRef{argFl, name + "__DOT__index", true};
AstLambdaArgRef* const valueArgRefp = new AstLambdaArgRef{argFl, name, false};
AstWith* const newp = new AstWith{nodep->fileline(), indexArgRefp, valueArgRefp,
nodep->exprsp()->unlinkFrBackWithNext()};
AstWith* const newp
= new AstWith{nodep->fileline(), indexArgRefp, valueArgRefp, exprOrConstraintsp};
funcrefp->addPinsp(newp);
}
funcrefp->addPinsp(argp);

View File

@ -533,17 +533,20 @@ size_t V3ParseImp::tokenPipeScanParam(size_t inDepth, bool forCell) {
return depth;
}
size_t V3ParseImp::tokenPipeScanTypeEq(size_t depth) {
size_t V3ParseImp::tokenPipeScanParens(size_t depth) {
// Search around IEEE type_reference to see if is expression
// Return location of following token, or input if not found
// yTYPE__ETC '(' ... ')' ['==' '===' '!=' '!===']
// ^- depth
// yRANDOMIZE yWITH '(' ... ')' ['{']
// ^- depth
if (tokenPeekp(depth)->token != '(') return depth;
depth += 1; // Past the (
int parens = 1; // Count first (
while (true) {
const int tok = tokenPeekp(depth)->token;
if (tok == 0) { // LCOV_EXCL_BR_LINE
UINFO(9, "tokenPipeScanTypeEq hit EOF; probably syntax error to come");
UINFO(9, "tokenPipeScanTypeParens hit EOF; probably syntax error to come");
break; // LCOV_EXCL_LINE
} else if (tok == '(') {
++parens;
@ -680,11 +683,11 @@ void V3ParseImp::tokenPipeline() {
}
} else if (token == yTYPE__LEX) {
VL_RESTORER(yylval); // Remember value, as about to read ahead
const size_t depth = tokenPipeScanTypeEq(0);
const size_t depth = tokenPipeScanParens(0);
const int postToken = tokenPeekp(depth)->token;
if ( // v-- token v-- postToken
// yTYPE__EQ '(' .... ')' EQ_OPERATOR yTYPE_ETC '(' ... ')'
postToken == yP_EQUAL || postToken == yP_NOTEQUAL || postToken == yP_CASEEQUAL
// v-- token v-- postToken
// yTYPE__EQ '(' .... ')' EQ_OPERATOR yTYPE_ETC '(' ... ')'
if (postToken == yP_EQUAL || postToken == yP_NOTEQUAL || postToken == yP_CASEEQUAL
|| postToken == yP_CASENOTEQUAL) {
token = yTYPE__EQ;
} else {
@ -704,7 +707,16 @@ void V3ParseImp::tokenPipeline() {
}
} else if (token == yWITH__LEX) {
if (nexttok == '(') {
token = yWITH__PAREN;
VL_RESTORER(yylval); // Remember value, as about to read ahead
const size_t depth = tokenPipeScanParens(0);
const int postToken = tokenPeekp(depth)->token;
// v-- token v-- postToken
// yWITH '(' .... ')' '{'
if (postToken == '{') {
token = yWITH__PAREN_CUR;
} else {
token = yWITH__PAREN;
}
} else if (nexttok == '[') {
token = yWITH__BRA;
} else if (nexttok == '{') {

View File

@ -314,7 +314,7 @@ private:
size_t tokenPipeScanIdType(size_t depth) VL_MT_DISABLED;
size_t tokenPipeScanBracket(size_t depth) VL_MT_DISABLED;
size_t tokenPipeScanParam(size_t depth, bool forInst) VL_MT_DISABLED;
size_t tokenPipeScanTypeEq(size_t depth) VL_MT_DISABLED;
size_t tokenPipeScanParens(size_t depth) VL_MT_DISABLED;
size_t tokenPipeScanEqNew(size_t depth) VL_MT_DISABLED;
const V3ParseBisonYYSType* tokenPeekp(size_t depth) VL_MT_DISABLED;
void preprocDumps(std::ostream& os, bool forInputs) VL_MT_DISABLED;

View File

@ -6726,11 +6726,13 @@ class WidthVisitor final : public VNVisitor {
void visit(AstWith* nodep) override {
// Should otherwise be underneath a method call
AstNodeDType* const vdtypep = m_vup->dtypeNullSkipRefp();
{
VL_RESTORER(m_withp);
m_withp = nodep;
userIterateChildren(nodep->indexArgRefp(), nullptr);
userIterateChildren(nodep->valueArgRefp(), nullptr);
VL_RESTORER(m_withp);
m_withp = nodep;
userIterateChildren(nodep->indexArgRefp(), nullptr);
userIterateChildren(nodep->valueArgRefp(), nullptr);
if (!nodep->exprp()) {
nodep->dtypeSetVoid();
} else {
if (!nodep->exprp()->hasDType()) {
userIterateAndNext(nodep->exprp(), nullptr);
} else {

View File

@ -589,6 +589,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> yWITH__ETC "with"
%token<fl> yWITH__LEX "with-in-lex"
%token<fl> yWITH__PAREN "with-then-("
%token<fl> yWITH__PAREN_CUR "with-then-(-then-{"
%token<fl> yWOR "wor"
%token<fl> yWREAL "wreal"
%token<fl> yXNOR "xnor"
@ -4052,7 +4053,8 @@ task_subroutine_callNoMethod<nodeExprp>: // function_subroutine_callNoMethod
// // IEEE: randomize_call
// // 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
| funcRef yWITH__CUR constraint_block { $$ = new AstWithParse{$2, $1, $3}; }
| funcRef yWITH__CUR constraint_block { $$ = new AstWithParse{$2, $1, nullptr, $3}; }
| funcRef yWITH__PAREN_CUR '(' expr ')' constraint_block { $$ = new AstWithParse{$2, $1, $4, $6}; }
;
function_subroutine_callNoMethod<nodeExprp>: // IEEE: function_subroutine_call (as function)
@ -4067,7 +4069,8 @@ function_subroutine_callNoMethod<nodeExprp>: // IEEE: function_subroutine
// // IEEE: randomize_call
// // 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
| funcRef yWITH__CUR constraint_block { $$ = new AstWithParse{$2, $1, $3}; }
| funcRef yWITH__CUR constraint_block { $$ = new AstWithParse{$2, $1, nullptr, $3}; }
| funcRef yWITH__PAREN_CUR '(' expr ')' constraint_block { $$ = new AstWithParse{$2, $1, $4, $6}; }
;
system_t_call<nodeStmtp>: // IEEE: system_tf_call (as task)

View File

@ -0,0 +1,8 @@
%Error-UNSUPPORTED: t/t_randomize_with_constraint.v:13:26: Unsupported: 'randomize with (...) {...}'
13 | return obj.randomize() with (
| ^~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_randomize_with_constraint.v:21:26: Unsupported: 'randomize with (...) {...}'
21 | return obj.randomize() with (
| ^~~~
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 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=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,36 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
class Cls;
rand int m_x;
int m_y = -1;
endclass
function int func1(Cls obj, int y);
return obj.randomize() with (
m_x) {
m_x > 0;
m_x < y;
};
endfunction
function int func2(Cls obj, int y);
return obj.randomize() with (
m_x) {
m_x > 0;
m_x < m_y;
};
endfunction
module t;
initial begin
Cls c;
int i;
c = new;
i = func1(c, 2);
i = func2(c, 2);
end
endmodule