[clang] 6e473ae - [clang-format] Put ports on separate lines in Verilog module headers
via cfe-commits
cfe-commits at lists.llvm.org
Sun Feb 19 19:34:01 PST 2023
Author: sstwcw
Date: 2023-02-20T03:24:13Z
New Revision: 6e473aeffdc1c26307e19f68252767a32e0047ad
URL: https://github.com/llvm/llvm-project/commit/6e473aeffdc1c26307e19f68252767a32e0047ad
DIFF: https://github.com/llvm/llvm-project/commit/6e473aeffdc1c26307e19f68252767a32e0047ad.diff
LOG: [clang-format] Put ports on separate lines in Verilog module headers
New:
```
module mh1
(input var int in1,
input var in2, in3,
output tagged_st out);
endmodule
```
Old:
```
module mh1
(input var int in1, input var in2, in3, output tagged_st out);
endmodule
```
`getNextNonComment` was modified to return a non-const pointer because
we needed to use it that way in `verilogGroupDecl`.
The comment on line 2626 was a typo. We corrected it while modifying
the function.
Reviewed By: MyDeveloperDay
Differential Revision: https://reviews.llvm.org/D143825
Added:
Modified:
clang/lib/Format/FormatToken.h
clang/lib/Format/TokenAnnotator.cpp
clang/unittests/Format/FormatTestVerilog.cpp
Removed:
################################################################################
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index d5a2a39094bea..d06a59e5ec1e4 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -152,8 +152,12 @@ namespace format {
TYPE(VerilogDimensionedTypeName) \
/* for the base in a number literal, not including the quote */ \
TYPE(VerilogNumberBase) \
+ /* like `(strong1, pull0)` */ \
+ TYPE(VerilogStrength) \
/* Things inside the table in user-defined primitives. */ \
TYPE(VerilogTableItem) \
+ /* those that separate ports of
diff erent types */ \
+ TYPE(VerilogTypeComma) \
TYPE(Unknown)
/// Determines the semantic type of a syntactic token, e.g. whether "<" is a
@@ -1792,6 +1796,27 @@ struct AdditionalKeywords {
kw_input, kw_output, kw_sequence)));
}
+ bool isVerilogQualifier(const FormatToken &Tok) const {
+ switch (Tok.Tok.getKind()) {
+ case tok::kw_extern:
+ case tok::kw_signed:
+ case tok::kw_static:
+ case tok::kw_unsigned:
+ case tok::kw_virtual:
+ return true;
+ case tok::identifier:
+ return Tok.isOneOf(
+ kw_let, kw_var, kw_ref, kw_automatic, kw_bins, kw_coverpoint,
+ kw_ignore_bins, kw_illegal_bins, kw_inout, kw_input, kw_interconnect,
+ kw_local, kw_localparam, kw_output, kw_parameter, kw_pure, kw_rand,
+ kw_randc, kw_scalared, kw_specparam, kw_tri, kw_tri0, kw_tri1,
+ kw_triand, kw_trior, kw_trireg, kw_uwire, kw_vectored, kw_wand,
+ kw_wildcard, kw_wire, kw_wor);
+ default:
+ return false;
+ }
+ }
+
private:
/// The JavaScript keywords beyond the C++ keyword set.
std::unordered_set<IdentifierInfo *> JsExtraKeywords;
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 34c51c240cd34..0461531c32b18 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -2596,8 +2596,21 @@ class ExpressionParser {
FormatToken *Start = Current;
FormatToken *LatestOperator = nullptr;
unsigned OperatorIndex = 0;
+ // The first name of the current type in a port list.
+ FormatToken *VerilogFirstOfType = nullptr;
while (Current) {
+ // In Verilog ports in a module header that don't have a type take the
+ // type of the previous one. For example,
+ // module a(output b,
+ // c,
+ // output d);
+ // In this case there need to be fake parentheses around b and c.
+ if (Style.isVerilog() && Precedence == prec::Comma) {
+ VerilogFirstOfType =
+ verilogGroupDecl(VerilogFirstOfType, LatestOperator);
+ }
+
// Consume operators with higher precedence.
parse(Precedence + 1);
@@ -2610,7 +2623,7 @@ class ExpressionParser {
Start = Current;
}
- // At the end of the line or when an operator with higher precedence is
+ // At the end of the line or when an operator with lower precedence is
// found, insert fake parenthesis and return.
if (!Current ||
(Current->closesScope() &&
@@ -2647,6 +2660,10 @@ class ExpressionParser {
}
}
+ // Group variables of the same type.
+ if (Style.isVerilog() && Precedence == prec::Comma && VerilogFirstOfType)
+ addFakeParenthesis(VerilogFirstOfType, prec::Comma);
+
if (LatestOperator && (Current || Precedence > 0)) {
// The requires clauses do not neccessarily end in a semicolon or a brace,
// but just go over to struct/class or a function declaration, we need to
@@ -2782,6 +2799,140 @@ class ExpressionParser {
}
}
+ // Add fake parenthesis around declarations of the same type for example in a
+ // module prototype. Return the first port / variable of the current type.
+ FormatToken *verilogGroupDecl(FormatToken *FirstOfType,
+ FormatToken *PreviousComma) {
+ if (!Current)
+ return nullptr;
+
+ FormatToken *Start = Current;
+
+ // Skip attributes.
+ while (Start->startsSequence(tok::l_paren, tok::star)) {
+ if (!(Start = Start->MatchingParen) ||
+ !(Start = Start->getNextNonComment())) {
+ return nullptr;
+ }
+ }
+
+ FormatToken *Tok = Start;
+
+ if (Tok->is(Keywords.kw_assign))
+ Tok = Tok->getNextNonComment();
+
+ // Skip any type qualifiers to find the first identifier. It may be either a
+ // new type name or a variable name. There can be several type qualifiers
+ // preceding a variable name, and we can not tell them apart by looking at
+ // the word alone since a macro can be defined as either a type qualifier or
+ // a variable name. Thus we use the last word before the dimensions instead
+ // of the first word as the candidate for the variable or type name.
+ FormatToken *First = nullptr;
+ while (Tok) {
+ FormatToken *Next = Tok->getNextNonComment();
+
+ if (Tok->is(tok::hash)) {
+ // Start of a macro expansion.
+ First = Tok;
+ Tok = Next;
+ if (Tok)
+ Tok = Tok->getNextNonComment();
+ } else if (Tok->is(tok::hashhash)) {
+ // Concatenation. Skip.
+ Tok = Next;
+ if (Tok)
+ Tok = Tok->getNextNonComment();
+ } else if ((Keywords.isVerilogQualifier(*Tok) ||
+ Keywords.isVerilogIdentifier(*Tok))) {
+ First = Tok;
+ Tok = Next;
+ // The name may have dots like `interface_foo.modport_foo`.
+ while (Tok && Tok->isOneOf(tok::period, tok::coloncolon) &&
+ (Tok = Tok->getNextNonComment())) {
+ if (Keywords.isVerilogIdentifier(*Tok))
+ Tok = Tok->getNextNonComment();
+ }
+ } else if (!Next) {
+ Tok = nullptr;
+ } else if (Tok->is(tok::l_paren)) {
+ // Make sure the parenthesized list is a drive strength. Otherwise the
+ // statement may be a module instantiation in which case we have already
+ // found the instance name.
+ if (Next->isOneOf(
+ Keywords.kw_highz0, Keywords.kw_highz1, Keywords.kw_large,
+ Keywords.kw_medium, Keywords.kw_pull0, Keywords.kw_pull1,
+ Keywords.kw_small, Keywords.kw_strong0, Keywords.kw_strong1,
+ Keywords.kw_supply0, Keywords.kw_supply1, Keywords.kw_weak0,
+ Keywords.kw_weak1)) {
+ Tok->setType(TT_VerilogStrength);
+ Tok = Tok->MatchingParen;
+ if (Tok) {
+ Tok->setType(TT_VerilogStrength);
+ Tok = Tok->getNextNonComment();
+ }
+ } else {
+ break;
+ }
+ } else if (Tok->is(tok::hash)) {
+ if (Next->is(tok::l_paren))
+ Next = Next->MatchingParen;
+ if (Next)
+ Tok = Next->getNextNonComment();
+ } else {
+ break;
+ }
+ }
+
+ // Find the second identifier. If it exists it will be the name.
+ FormatToken *Second = nullptr;
+ // Dimensions.
+ while (Tok && Tok->is(tok::l_square) && (Tok = Tok->MatchingParen))
+ Tok = Tok->getNextNonComment();
+ if (Tok && (Tok->is(tok::hash) || Keywords.isVerilogIdentifier(*Tok)))
+ Second = Tok;
+
+ // If the second identifier doesn't exist and there are qualifiers, the type
+ // is implied.
+ FormatToken *TypedName = nullptr;
+ if (Second) {
+ TypedName = Second;
+ if (First && First->is(TT_Unknown))
+ First->setType(TT_VerilogDimensionedTypeName);
+ } else if (First != Start) {
+ // If 'First' is null, then this isn't a declaration, 'TypedName' gets set
+ // to null as intended.
+ TypedName = First;
+ }
+
+ if (TypedName) {
+ // This is a declaration with a new type.
+ if (TypedName->is(TT_Unknown))
+ TypedName->setType(TT_StartOfName);
+ // Group variables of the previous type.
+ if (FirstOfType && PreviousComma) {
+ PreviousComma->setType(TT_VerilogTypeComma);
+ addFakeParenthesis(FirstOfType, prec::Comma, PreviousComma->Previous);
+ }
+
+ FirstOfType = TypedName;
+
+ // Don't let higher precedence handle the qualifiers. For example if we
+ // have:
+ // parameter x = 0
+ // We skip `parameter` here. This way the fake parentheses for the
+ // assignment will be around `x = 0`.
+ while (Current && Current != FirstOfType) {
+ if (Current->opensScope()) {
+ next();
+ parse();
+ }
+ next();
+ }
+ }
+
+ return FirstOfType;
+ }
+
const FormatStyle &Style;
const AdditionalKeywords &Keywords;
const AnnotatedLine &Line;
@@ -4213,6 +4364,9 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
// Add space in attribute like `(* ASYNC_REG = "TRUE" *)`.
if (Left.endsSequence(tok::star, tok::l_paren) && Right.is(tok::identifier))
return true;
+ // Add space before drive strength like in `wire (strong1, pull0)`.
+ if (Right.is(tok::l_paren) && Right.is(TT_VerilogStrength))
+ return true;
}
if (Left.is(TT_ImplicitStringLiteral))
return Right.hasWhitespaceBefore();
@@ -4524,6 +4678,9 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
return true;
}
} else if (Style.isVerilog()) {
+ // Break between ports of
diff erent types.
+ if (Left.is(TT_VerilogTypeComma))
+ return true;
// Break after labels. In Verilog labels don't have the 'case' keyword, so
// it is hard to identify them in UnwrappedLineParser.
if (!Keywords.isVerilogBegin(Right) && Keywords.isVerilogEndOfLabel(Left))
diff --git a/clang/unittests/Format/FormatTestVerilog.cpp b/clang/unittests/Format/FormatTestVerilog.cpp
index 7931a31decfa5..fc23dd8ea1d0a 100644
--- a/clang/unittests/Format/FormatTestVerilog.cpp
+++ b/clang/unittests/Format/FormatTestVerilog.cpp
@@ -252,6 +252,34 @@ TEST_F(FormatTestVerilog, Case) {
Style);
}
+TEST_F(FormatTestVerilog, Declaration) {
+ verifyFormat("wire mynet;");
+ verifyFormat("wire mynet, mynet1;");
+ verifyFormat("wire mynet, //\n"
+ " mynet1;");
+ verifyFormat("wire mynet = enable;");
+ verifyFormat("wire mynet = enable, mynet1;");
+ verifyFormat("wire mynet = enable, //\n"
+ " mynet1;");
+ verifyFormat("wire mynet, mynet1 = enable;");
+ verifyFormat("wire mynet, //\n"
+ " mynet1 = enable;");
+ verifyFormat("wire mynet = enable, mynet1 = enable;");
+ verifyFormat("wire mynet = enable, //\n"
+ " mynet1 = enable;");
+ verifyFormat("wire (strong1, pull0) mynet;");
+ verifyFormat("wire (strong1, pull0) mynet, mynet1;");
+ verifyFormat("wire (strong1, pull0) mynet, //\n"
+ " mynet1;");
+ verifyFormat("wire (strong1, pull0) mynet = enable;");
+ verifyFormat("wire (strong1, pull0) mynet = enable, mynet1;");
+ verifyFormat("wire (strong1, pull0) mynet = enable, //\n"
+ " mynet1;");
+ verifyFormat("wire (strong1, pull0) mynet, mynet1 = enable;");
+ verifyFormat("wire (strong1, pull0) mynet, //\n"
+ " mynet1 = enable;");
+}
+
TEST_F(FormatTestVerilog, Delay) {
// Delay by the default unit.
verifyFormat("#0;");
@@ -275,6 +303,155 @@ TEST_F(FormatTestVerilog, Delay) {
"x = x;"));
}
+TEST_F(FormatTestVerilog, Headers) {
+ // Test headers with multiple ports.
+ verifyFormat("module mh1\n"
+ " (input var int in1,\n"
+ " input var shortreal in2,\n"
+ " output tagged_st out);\n"
+ "endmodule");
+ // Ports should be grouped by types.
+ verifyFormat("module test\n"
+ " (input [7 : 0] a,\n"
+ " input signed [7 : 0] b, c, d);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " (input [7 : 0] a,\n"
+ " (* x = x *) input signed [7 : 0] b, c, d);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " (input [7 : 0] a = 0,\n"
+ " input signed [7 : 0] b = 0, c = 0, d = 0);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " #(parameter x)\n"
+ " (input [7 : 0] a,\n"
+ " input signed [7 : 0] b, c, d);\n"
+ "endmodule");
+ // When a line needs to be broken, ports of the same type should be aligned to
+ // the same column.
+ verifyFormat("module test\n"
+ " (input signed [7 : 0] b, c, //\n"
+ " d);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " ((* x = x *) input signed [7 : 0] b, c, //\n"
+ " d);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " (input signed [7 : 0] b = 0, c, //\n"
+ " d);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " (input signed [7 : 0] b, c = 0, //\n"
+ " d);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " (input signed [7 : 0] b, c, //\n"
+ " d = 0);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " (input wire logic signed [7 : 0][0 : 1] b, c, //\n"
+ " d);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " (input signed [7 : 0] b, //\n"
+ " c, //\n"
+ " d);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " (input [7 : 0] a,\n"
+ " input signed [7 : 0] b, //\n"
+ " c, //\n"
+ " d);\n"
+ "endmodule");
+ verifyFormat("module test\n"
+ " (input signed [7 : 0] b, //\n"
+ " c, //\n"
+ " d,\n"
+ " output signed [7 : 0] h);\n"
+ "endmodule");
+ // With a modport.
+ verifyFormat("module m\n"
+ " (i2.master i);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2.master i, ii);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2.master i, //\n"
+ " ii);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2.master i,\n"
+ " input ii);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2::i2.master i);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2::i2.master i, ii);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2::i2.master i, //\n"
+ " ii);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2::i2.master i,\n"
+ " input ii);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2::i2 i);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2::i2 i, ii);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2::i2 i, //\n"
+ " ii);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (i2::i2 i,\n"
+ " input ii);\n"
+ "endmodule");
+ // With a macro in the names.
+ verifyFormat("module m\n"
+ " (input var `x a, b);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (input var `x a, //\n"
+ " b);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (input var x `a, b);\n"
+ "endmodule");
+ verifyFormat("module m\n"
+ " (input var x `a, //\n"
+ " b);\n"
+ "endmodule");
+ // With a concatenation in the names.
+ auto Style = getLLVMStyle(FormatStyle::LK_Verilog);
+ Style.ColumnLimit = 40;
+ verifyFormat("`define X(x) \\\n"
+ " module test \\\n"
+ " (input var x``x a, b);",
+ Style);
+ verifyFormat("`define X(x) \\\n"
+ " module test \\\n"
+ " (input var x``x aaaaaaaaaaaaaaa, \\\n"
+ " b);",
+ Style);
+ verifyFormat("`define X(x) \\\n"
+ " module test \\\n"
+ " (input var x a``x, b);",
+ Style);
+ verifyFormat("`define X(x) \\\n"
+ " module test \\\n"
+ " (input var x aaaaaaaaaaaaaaa``x, \\\n"
+ " b);",
+ Style);
+}
+
TEST_F(FormatTestVerilog, Hierarchy) {
verifyFormat("module x;\n"
"endmodule");
More information about the cfe-commits
mailing list