From 6e57076726381c786a576e16fa561d5f9314859e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 9 Jun 2020 22:05:06 -0400 Subject: [PATCH] Support --bbox-unsup parsing of nested foreach. --- src/V3AstNodes.h | 25 ++++++++++--- src/V3LinkParse.cpp | 47 +++++++++++++++++++++--- src/verilog.y | 61 +++++++++++++++++++++----------- test_regress/t/t_foreach.v | 25 ++++++++++++- test_regress/t/t_foreach_bad.out | 7 ++++ test_regress/t/t_foreach_bad.pl | 19 ++++++++++ test_regress/t/t_foreach_bad.v | 22 ++++++++++++ 7 files changed, 176 insertions(+), 30 deletions(-) create mode 100644 test_regress/t/t_foreach_bad.out create mode 100755 test_regress/t/t_foreach_bad.pl create mode 100644 test_regress/t/t_foreach_bad.v diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index f42e159d9..2a9f00e65 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -1560,6 +1560,25 @@ public: virtual bool same(const AstNode* samep) const { return true; } }; +class AstSelLoopVars : public AstNode { + // Parser only concept "[id, id, id]" for a foreach statement + // Unlike normal selects elements is a list +public: + AstSelLoopVars(FileLine* fl, AstNode* fromp, AstNode* elementsp) + : ASTGEN_SUPER(fl) { + setOp1p(fromp); + addNOp2p(elementsp); + } + ASTNODE_NODE_FUNCS(SelLoopVars) + virtual string emitC() { V3ERROR_NA_RETURN(""); } + virtual string emitVerilog() { V3ERROR_NA_RETURN(""); } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(const AstNode* samep) const { return true; } + virtual bool maybePointedTo() const { return false; } + AstNode* fromp() const { return op1p(); } + AstNode* elementsp() const { return op2p(); } +}; + class AstSelExtract : public AstNodePreSel { // Range extraction, gets replaced with AstSel public: @@ -4224,15 +4243,13 @@ public: class AstForeach : public AstNodeStmt { public: - AstForeach(FileLine* fl, AstNode* arrayp, AstNode* varsp, AstNode* bodysp) + AstForeach(FileLine* fl, AstNode* arrayp, AstNode* bodysp) : ASTGEN_SUPER(fl) { setOp1p(arrayp); - addNOp2p(varsp); addNOp4p(bodysp); } ASTNODE_NODE_FUNCS(Foreach) - AstNode* arrayp() const { return op1p(); } // op1 = array - AstNode* varsp() const { return op2p(); } // op2 = variable index list + AstNode* arrayp() const { return op1p(); } // op1 = array and index vars AstNode* bodysp() const { return op4p(); } // op4 = body of loop virtual bool isGateOptimizable() const { return false; } virtual int instrCount() const { return instrCountBranch(); } diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index dd89317ad..64b430058 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -385,11 +385,50 @@ private: // FOREACH(array,loopvars,body) // -> BEGIN(declare vars, loopa=lowest; WHILE(loopa<=highest, ... body)) // nodep->dumpTree(cout, "-foreach-old:"); - AstNode* newp = nodep->bodysp()->unlinkFrBackWithNext(); - AstNode* arrayp = nodep->arrayp(); - int dimension = 1; + UINFO(9, "FOREACH " << nodep << endl); + // nodep->dumpTree(cout, "-foreach-in:"); + AstNode* newp = nodep->bodysp(); + if (newp) newp->unlinkFrBackWithNext(); + // Separate iteration vars from base from variable + // Input: + // v--- arrayp + // 1. DOT(DOT(first, second), ASTSELLOOPVARS(third, var0..var1)) + // Separated: + // bracketp = ASTSELLOOPVARS(...) + // arrayp = DOT(DOT(first, second), third) + // firstVarp = var0..var1 + // Other examples + // 2. ASTSELBIT(first, var0)) + // 3. ASTSELLOOPVARS(first, var0..var1)) + // 4. DOT(DOT(first, second), ASTSELBIT(third, var0)) + AstNode* bracketp = nodep->arrayp(); + AstNode* firstVarsp = NULL; + while (AstDot* dotp = VN_CAST(bracketp, Dot)) { bracketp = dotp->rhsp(); } + if (AstSelBit* selp = VN_CAST(bracketp, SelBit)) { + firstVarsp = selp->rhsp()->unlinkFrBackWithNext(); + selp->replaceWith(selp->fromp()->unlinkFrBack()); + VL_DO_DANGLING(selp->deleteTree(), selp); + } else if (AstSelLoopVars* selp = VN_CAST(bracketp, SelLoopVars)) { + firstVarsp = selp->elementsp()->unlinkFrBackWithNext(); + selp->replaceWith(selp->fromp()->unlinkFrBack()); + VL_DO_DANGLING(selp->deleteTree(), selp); + } else { + nodep->v3error( + "Syntax error; foreach missing bracketed index variable (IEEE 1800-2017 12.7.3)"); + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + return; + } + AstNode* arrayp = nodep->arrayp(); // Maybe different node since bracketp looked + if (!VN_IS(arrayp, ParseRef)) { + // Code below needs to use other then attributes to figure out the bounds + // Also need to deal with queues, etc + arrayp->v3warn(E_UNSUPPORTED, "Unsupported: foreach on non-simple variable reference"); + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + return; + } + // Split into for loop // Must do innermost (last) variable first - AstNode* firstVarsp = nodep->varsp()->unlinkFrBackWithNext(); + int dimension = 1; AstNode* lastVarsp = firstVarsp; while (lastVarsp->nextp()) { lastVarsp = lastVarsp->nextp(); diff --git a/src/verilog.y b/src/verilog.y index 2013bf334..9dd91ba17 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3062,7 +3062,7 @@ statement_item: // IEEE: statement_item } else $$ = new AstWhile($1,$5,NULL); } // // IEEE says array_identifier here, but dotted accepted in VMM and 1800-2009 - | yFOREACH '(' idClassForeach '[' loop_variables ']' ')' stmtBlock { $$ = new AstForeach($1,$3,$5,$8); } + | yFOREACH '(' idClassSelForeach ')' stmtBlock { $$ = new AstForeach($1, $3, $5); } // // // IEEE: jump_statement | yRETURN ';' { $$ = new AstReturn($1); } @@ -4735,48 +4735,67 @@ idClassSel: // Misc Ref to dotted, and/or arrayed, and/or bit-ranged va | packageClassScope idDotted { $$ = $2; BBUNSUP($2, "Unsupported: package scoped id"); } ; +idClassSelForeach: + idDottedForeach { $$ = $1; } + // // IEEE: [ implicit_class_handle . | package_scope ] hierarchical_variable_identifier select + | yTHIS '.' idDottedForeach { $$ = $3; BBUNSUP($1, "Unsupported: this"); } + | ySUPER '.' idDottedForeach { $$ = $3; BBUNSUP($1, "Unsupported: super"); } + | yTHIS '.' ySUPER '.' idDottedForeach { $$ = $5; BBUNSUP($1, "Unsupported: this.super"); } + // // Expanded: package_scope idForeach + | packageClassScope idDottedForeach { $$ = $2; BBUNSUP($2, "Unsupported: package/class scoped id"); } + ; + idDotted: yD_ROOT '.' idDottedMore { $$ = new AstDot($2, false, new AstParseRef($1, VParseRefExp::PX_ROOT, "$root"), $3); } | idDottedMore { $$ = $1; } ; +idDottedForeach: + yD_ROOT '.' idDottedMoreForeach + { $$ = new AstDot($2, false, new AstParseRef($1, VParseRefExp::PX_ROOT, "$root"), $3); } + | idDottedMoreForeach { $$ = $1; } + ; + idDottedMore: idArrayed { $$ = $1; } | idDottedMore '.' idArrayed { $$ = new AstDot($2, false, $1, $3); } ; +idDottedMoreForeach: + idArrayedForeach { $$ = $1; } + | idDottedMoreForeach '.' idArrayedForeach { $$ = new AstDot($2, false, $1, $3); } + ; + // Single component of dotted path, maybe [#]. // Due to lookahead constraints, we can't know if [:] or [+:] are valid (last dotted part), // we'll assume so and cleanup later. // id below includes: // enum_identifier idArrayed: // IEEE: id + select - id { $$ = new AstParseRef($1,VParseRefExp::PX_TEXT,*$1,NULL,NULL); } + id { $$ = new AstParseRef($1, VParseRefExp::PX_TEXT, *$1, NULL, NULL); } // // IEEE: id + part_select_range/constant_part_select_range - | idArrayed '[' expr ']' { $$ = new AstSelBit($2,$1,$3); } // Or AstArraySel, don't know yet. - | idArrayed '[' constExpr ':' constExpr ']' { $$ = new AstSelExtract($2,$1,$3,$5); } + | idArrayed '[' expr ']' { $$ = new AstSelBit($2, $1, $3); } // Or AstArraySel, don't know yet. + | idArrayed '[' constExpr ':' constExpr ']' { $$ = new AstSelExtract($2, $1, $3, $5); } // // IEEE: id + indexed_range/constant_indexed_range - | idArrayed '[' expr yP_PLUSCOLON constExpr ']' { $$ = new AstSelPlus($2,$1,$3,$5); } - | idArrayed '[' expr yP_MINUSCOLON constExpr ']' { $$ = new AstSelMinus($2,$1,$3,$5); } + | idArrayed '[' expr yP_PLUSCOLON constExpr ']' { $$ = new AstSelPlus($2, $1, $3, $5); } + | idArrayed '[' expr yP_MINUSCOLON constExpr ']' { $$ = new AstSelMinus($2, $1, $3, $5); } ; -idClassForeach: - idForeach { $$ = $1; } - // // IEEE: [ implicit_class_handle . | package_scope ] hierarchical_variable_identifier select - | yTHIS '.' idForeach { $$ = $3; BBUNSUP($1, "Unsupported: this"); } - | ySUPER '.' idForeach { $$ = $3; BBUNSUP($1, "Unsupported: super"); } - | yTHIS '.' ySUPER '.' idForeach { $$ = $5; BBUNSUP($1, "Unsupported: this.super"); } - // // Expanded: package_scope idForeach - | packageClassScope idForeach { $$ = $2; BBUNSUP($2, "Unsupported: package/class scoped id"); } +idArrayedForeach: // IEEE: id + select (under foreach expression) + id { $$ = new AstParseRef($1, VParseRefExp::PX_TEXT, *$1, NULL, NULL); } + // // IEEE: id + part_select_range/constant_part_select_range + | idArrayed '[' expr ']' { $$ = new AstSelBit($2, $1, $3); } // Or AstArraySel, don't know yet. + | idArrayed '[' constExpr ':' constExpr ']' { $$ = new AstSelExtract($2, $1, $3, $5); } + // // IEEE: id + indexed_range/constant_indexed_range + | idArrayed '[' expr yP_PLUSCOLON constExpr ']' { $$ = new AstSelPlus($2, $1, $3, $5); } + | idArrayed '[' expr yP_MINUSCOLON constExpr ']' { $$ = new AstSelMinus($2, $1, $3, $5); } + // // IEEE: loop_variables (under foreach expression) + // // To avoid conflicts we allow expr as first element, must post-check + | idArrayed '[' expr ',' loop_variables ']' + { $3 = AstNode::addNextNull($3, $5); $$ = new AstSelLoopVars($2, $1, $3); } ; -idForeach: - varRefBase { $$ = $1; } - | idForeach '.' varRefBase { $$ = new AstDot($2, false, $1, $3); } - ; - - // VarRef without any dots or vectorizaion varRefBase: id { $$ = new AstVarRef($1,*$1,false);} @@ -6079,7 +6098,7 @@ constraint_expression: // ==IEEE: constraint_expression | yIF '(' expr ')' constraint_set %prec prLOWER_THAN_ELSE { $$ = NULL; /*UNSUP*/ } | yIF '(' expr ')' constraint_set yELSE constraint_set { $$ = NULL; /*UNSUP*/ } // // IEEE says array_identifier here, but dotted accepted in VMM + 1800-2009 - | yFOREACH '(' idClassForeach '[' loop_variables ']' ')' constraint_set { $$ = NULL; /*UNSUP*/ } + | yFOREACH '(' idClassSelForeach ')' constraint_set { $$ = NULL; /*UNSUP*/ } // // soft is 1800-2012 | yDISABLE ySOFT expr/*constraint_primary*/ ';' { $$ = NULL; /*UNSUP*/ } ; diff --git a/test_regress/t/t_foreach.v b/test_regress/t/t_foreach.v index 7055386c1..3d9b26afa 100644 --- a/test_regress/t/t_foreach.v +++ b/test_regress/t/t_foreach.v @@ -11,11 +11,20 @@ module t (/*AUTOARG*/); // verilator lint_off LITENDIAN // verilator lint_off WIDTH - reg [63:0] sum; + reg [63:0] sum; // Checked not in objects + reg [63:0] add; reg [2:1] [4:3] array [5:6] [7:8]; reg [1:2] [3:4] larray [6:5] [8:7]; bit [31:0] depth1_array [0:0]; + typedef struct packed { + reg [1:0] [63:0] subarray; + } str_t; + typedef struct packed { + str_t mid; + } mid_t; + mid_t strarray[3]; + function [63:0] crc (input [63:0] sum, input [31:0] a, input [31:0] b, input [31:0] c, input [31:0] d); crc = {sum[62:0],sum[63]} ^ {4'b0,a[7:0], 4'h0,b[7:0], 4'h0,c[7:0], 4'h0,d[7:0]}; endfunction @@ -87,6 +96,20 @@ module t (/*AUTOARG*/); end `checkh(sum, 64'h0020179aa7aa0aaa); + add = 0; + strarray[0].mid.subarray[0] = 1; + strarray[0].mid.subarray[1] = 2; + strarray[1].mid.subarray[0] = 4; + strarray[1].mid.subarray[1] = 5; + strarray[2].mid.subarray[0] = 6; + strarray[2].mid.subarray[1] = 7; +`ifndef VERILATOR // Unsupported + foreach (strarray[s]) + foreach (strarray[s].mid.subarray[ss]) + add += strarray[s].mid.subarray[ss]; + `checkh(add, 'h19); +`endif + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_foreach_bad.out b/test_regress/t/t_foreach_bad.out new file mode 100644 index 000000000..0a9fed94a --- /dev/null +++ b/test_regress/t/t_foreach_bad.out @@ -0,0 +1,7 @@ +%Error: t/t_foreach_bad.v:14:7: Syntax error; foreach missing bracketed index variable (IEEE 1800-2017 12.7.3) + 14 | foreach (array); + | ^~~~~~~ +%Error-UNSUPPORTED: t/t_foreach_bad.v:16:21: Unsupported: foreach on non-simple variable reference + 16 | foreach (array.array[a]); + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_foreach_bad.pl b/test_regress/t/t_foreach_bad.pl new file mode 100755 index 000000000..a60503a1f --- /dev/null +++ b/test_regress/t/t_foreach_bad.pl @@ -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 2003 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; diff --git a/test_regress/t/t_foreach_bad.v b/test_regress/t/t_foreach_bad.v new file mode 100644 index 000000000..be94993af --- /dev/null +++ b/test_regress/t/t_foreach_bad.v @@ -0,0 +1,22 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + + integer a, b; + + reg [2:0][2:0] array; + + initial begin + foreach (array); // no index + + foreach (array.array[a]); // not supported + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule