[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