summaryrefslogtreecommitdiff
path: root/examples/verilog_parse.py
diff options
context:
space:
mode:
Diffstat (limited to 'examples/verilog_parse.py')
-rw-r--r--examples/verilog_parse.py946
1 files changed, 946 insertions, 0 deletions
diff --git a/examples/verilog_parse.py b/examples/verilog_parse.py
new file mode 100644
index 0000000..6a80811
--- /dev/null
+++ b/examples/verilog_parse.py
@@ -0,0 +1,946 @@
+#
+# verilogParse.py
+#
+# an example of using the pyparsing module to be able to process Verilog files
+# uses BNF defined at http://www.verilog.com/VerilogBNF.html
+#
+# Copyright (c) 2004-2011 Paul T. McGuire. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# If you find this software to be useful, please make a donation to one
+# of the following charities:
+# - the Red Cross (https://www.redcross.org/)
+# - Hospice Austin (https://www.hospiceaustin.org/)
+#
+# DISCLAIMER:
+# THIS SOFTWARE IS PROVIDED BY PAUL T. McGUIRE ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+# EVENT SHALL PAUL T. McGUIRE OR CO-CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OFUSE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# For questions or inquiries regarding this license, or commercial use of
+# this software, contact the author via e-mail: ptmcg@users.sourceforge.net
+#
+# Todo:
+# - add pre-process pass to implement compilerDirectives (ifdef, include, etc.)
+#
+# Revision History:
+#
+# 1.0 - Initial release
+# 1.0.1 - Fixed grammar errors:
+# . real declaration was incorrect
+# . tolerant of '=>' for '*>' operator
+# . tolerant of '?' as hex character
+# . proper handling of mintypmax_expr within path delays
+# 1.0.2 - Performance tuning (requires pyparsing 1.3)
+# 1.0.3 - Performance updates, using Regex (requires pyparsing 1.4)
+# 1.0.4 - Performance updates, enable packrat parsing (requires pyparsing 1.4.2)
+# 1.0.5 - Converted keyword Literals to Keywords, added more use of Group to
+# group parsed results tokens
+# 1.0.6 - Added support for module header with no ports list (thanks, Thomas Dejanovic!)
+# 1.0.7 - Fixed erroneous '<<' Forward definition in timCheckCond, omitting ()'s
+# 1.0.8 - Re-released under MIT license
+# 1.0.9 - Enhanced udpInstance to handle identifiers with leading '\' and subscripting
+# 1.0.10 - Fixed change added in 1.0.9 to work for all identifiers, not just those used
+# for udpInstance.
+# 1.0.11 - Fixed bug in inst_args, content alternatives were reversed
+# 1.1.0 - Some performance fixes, convert most literal strs to Keywords
+#
+from pathlib import Path
+import pprint
+import time
+
+__version__ = "1.1.0"
+
+__all__ = ["__version__", "verilogbnf"]
+
+from pyparsing import (
+ Literal,
+ Keyword,
+ Word,
+ Forward,
+ DelimitedList,
+ Group,
+ Optional,
+ Combine,
+ alphas,
+ nums,
+ restOfLine,
+ alphanums,
+ dbl_quoted_string,
+ empty,
+ ParseException,
+ one_of,
+ StringEnd,
+ FollowedBy,
+ ParserElement,
+ Regex,
+ cppStyleComment,
+)
+import pyparsing
+
+usePackrat = True
+
+packratOn = False
+if usePackrat:
+ try:
+ ParserElement.enable_packrat()
+ except Exception:
+ pass
+ else:
+ packratOn = True
+
+
+verilogbnf = None
+
+
+def make_verilog_bnf():
+ global verilogbnf
+
+ if verilogbnf is None:
+
+ # compiler directives
+ compilerDirective = Combine(
+ "`"
+ + one_of(
+ "define undef ifdef else endif default_nettype"
+ " include resetall timescale unconnected_drive"
+ " nounconnected_drive celldefine endcelldefine",
+ as_keyword=True,
+ )
+ + restOfLine
+ ).set_name("compilerDirective")
+
+ # primitives
+ (
+ SEMI,
+ COLON,
+ LPAR,
+ RPAR,
+ LBRACE,
+ RBRACE,
+ LBRACK,
+ RBRACK,
+ DOT,
+ COMMA,
+ EQ,
+ ) = Literal.using_each(";:(){}[].,=")
+
+ identLead = alphas + "$_"
+ identBody = alphanums + "$_"
+ identifier1 = Regex(
+ rf"\.?[{identLead}][{identBody}]*(\.[{identLead}][{identBody}]*)*"
+ ).set_name("baseIdent")
+ identifier2 = (
+ Regex(r"\\\S+").setParseAction(lambda t: t[0][1:]).set_name("escapedIdent")
+ ) # .setDebug()
+ identifier = identifier1 | identifier2
+ assert identifier2 == r"\abc"
+
+ hexnums = nums + "abcdefABCDEF" + "_?"
+ base = Regex("'[bBoOdDhH]").set_name("base")
+ basedNumber = Combine(
+ (Word(nums + "_") | "") + base + Word(hexnums + "xXzZ"),
+ joinString=" ",
+ adjacent=False,
+ ).set_name("basedNumber")
+ # number = ( basedNumber | Combine( Word( "+-"+spacedNums, spacedNums ) +
+ # Optional( DOT + Optional( Word( spacedNums ) ) ) +
+ # Optional( e + Word( "+-"+spacedNums, spacedNums ) ) ).set_name("numeric") )
+ number = (
+ basedNumber | Regex(r"[+-]?[0-9_]+(\.[0-9_]*)?([Ee][+-]?[0-9_]+)?")
+ ).set_name("numeric")
+
+ expr = Forward().set_name("expr")
+ concat = Group(LBRACE + DelimitedList(expr) + RBRACE)
+ multiConcat = Group("{" + expr + concat + "}").set_name("multiConcat")
+ funcCall = Group(
+ identifier + LPAR + (DelimitedList(expr) | "") + RPAR
+ ).set_name("funcCall")
+
+ subscrRef = Group(LBRACK + DelimitedList(expr, COLON) + RBRACK)
+ subscrIdentifier = Group(identifier + (subscrRef | ""))
+ # scalarConst = "0" | (( FollowedBy('1') + one_of("1'b0 1'b1 1'bx 1'bX 1'B0 1'B1 1'Bx 1'BX 1") ))
+ scalarConst = Regex("0|1('[Bb][01xX])?")
+ mintypmaxExpr = Group(expr + COLON + expr + COLON + expr).set_name("mintypmax")
+ primary = (
+ number
+ | (LPAR + mintypmaxExpr + RPAR)
+ | (LPAR + Group(expr) + RPAR).set_name("nestedExpr")
+ | multiConcat
+ | concat
+ | dbl_quoted_string
+ | funcCall
+ | subscrIdentifier
+ )
+
+ unop = one_of("+ - ! ~ & ~& | ^| ^ ~^").set_name("unop")
+ binop = one_of(
+ "+ - * / % == != === !== && "
+ "|| < <= > >= & | ^ ^~ >> << ** <<< >>>"
+ ).set_name("binop")
+
+ expr <<= (
+ (unop + expr)
+ | (primary + "?" + expr + COLON + expr) # must be first!
+ | (primary + ((binop + expr) | ""))
+ )
+
+ lvalue = subscrIdentifier | concat
+
+ # keywords
+ reg = Keyword("reg")
+ trireg = Keyword("trireg")
+ signed = Keyword("signed")
+ parameter = Keyword("parameter")
+ input_, output, inout = Keyword.using_each("input output inout".split())
+ time = Keyword("time")
+ integer = Keyword("integer")
+ real = Keyword("real")
+ event = Keyword("event")
+ scalared = Keyword("scalared")
+ vectored = Keyword("vectored")
+ if_ = Keyword("if")
+ else_ = Keyword("else")
+ always = Keyword("always")
+ initial = Keyword("initial")
+ small, medium, large = Keyword.using_each("small medium large".split())
+ edge = Keyword("edge")
+ posedge = Keyword("posedge")
+ negedge = Keyword("negedge")
+ specify, endspecify = Keyword.using_each("specify endspecify".split())
+ primitive, endprimitive = Keyword.using_each("primitive endprimitive".split())
+ fork = Keyword("fork")
+ join = Keyword("join")
+ begin = Keyword("begin")
+ end = Keyword("end")
+ default = Keyword("default")
+ forever = Keyword("forever")
+ repeat = Keyword("repeat")
+ while_ = Keyword("while")
+ for_ = Keyword("for")
+ case = one_of("case casez casex", as_keyword=True)
+ endcase = Keyword("endcase")
+ wait = Keyword("wait")
+ disable = Keyword("disable")
+ deassign = Keyword("deassign")
+ force = Keyword("force")
+ release = Keyword("release")
+ assign = Keyword("assign")
+ table, endtable = Keyword.using_each("table endtable".split())
+ function, endfunction = Keyword.using_each("function endfunction".split())
+ task, endtask = Keyword.using_each("task endtask".split())
+ module, macromodule, endmodule = Keyword.using_each(
+ "module macromodule endmodule".split()
+ )
+
+ eventExpr = Forward()
+ eventTerm = (
+ (posedge + expr) | (negedge + expr) | expr | (LPAR + eventExpr + RPAR)
+ )
+ eventExpr <<= Group(DelimitedList(eventTerm, Keyword("or")))
+ eventControl = Group(
+ "@" + ((LPAR + eventExpr + RPAR) | identifier | "*")
+ ).set_name("eventCtrl")
+
+ delayArg = (
+ number
+ | Word(alphanums + "$_")
+ | (LPAR + Group(DelimitedList(mintypmaxExpr | expr)) + RPAR) # identifier |
+ ).set_name(
+ "delayArg"
+ ) # .setDebug()
+ delay = Group("#" + delayArg).set_name("delay") # .setDebug()
+ delayOrEventControl = delay | eventControl
+
+ assgnmt = Group(lvalue + EQ + (delayOrEventControl | "") + expr).set_name(
+ "assgnmt"
+ )
+ nbAssgnmt = Group(
+ (lvalue + "<=" + (delay | "") + expr)
+ | (lvalue + "<=" + (eventControl | "") + expr)
+ ).set_name("nbassgnmt")
+
+ range_ = LBRACK + expr + COLON + expr + RBRACK
+
+ paramAssgnmt = Group(identifier + EQ + expr).set_name("paramAssgnmt")
+ parameterDecl = Group(
+ parameter + (range_ | "") + DelimitedList(paramAssgnmt) + SEMI
+ ).set_name("paramDecl")
+
+ inputDecl = Group(input_ + (range_ | "") + DelimitedList(identifier) + SEMI)
+ outputDecl = Group(output + (range_ | "") + DelimitedList(identifier) + SEMI)
+ inoutDecl = Group(inout + (range_ | "") + DelimitedList(identifier) + SEMI)
+
+ regIdentifier = Group(identifier + (LBRACK + expr + COLON + expr + RBRACK | ""))
+ regDecl = Group(
+ reg + (signed | "") + (range_ | "") + DelimitedList(regIdentifier) + SEMI
+ ).set_name("regDecl")
+ timeDecl = Group(time + DelimitedList(regIdentifier) + SEMI)
+ integerDecl = Group(integer + DelimitedList(regIdentifier) + SEMI)
+
+ strength0 = one_of("supply0 strong0 pull0 weak0 highz0", as_keyword=True)
+ strength1 = one_of("supply1 strong1 pull1 weak1 highz1", as_keyword=True)
+ driveStrength = Group(
+ LPAR
+ + ((strength0 + COMMA + strength1) | (strength1 + COMMA + strength0))
+ + RPAR
+ ).set_name("driveStrength")
+ nettype = one_of(
+ "wire tri tri1 supply0 wand triand tri0 supply1 wor trior trireg",
+ as_keyword=True,
+ )
+ expandRange = (scalared | vectored | "") + range_
+ realDecl = Group(real + DelimitedList(identifier) + SEMI)
+
+ eventDecl = Group(event + DelimitedList(identifier) + SEMI)
+
+ blockDecl = (
+ parameterDecl | regDecl | integerDecl | realDecl | timeDecl | eventDecl
+ )
+
+ stmt = Forward().set_name("stmt") # .setDebug()
+ stmtOrNull = stmt | SEMI
+ caseItem = (DelimitedList(expr) + COLON + stmtOrNull) | (
+ default + Optional(":") + stmtOrNull
+ )
+ stmt <<= Group(
+ (begin + Group(stmt[...:end]) + end).set_name("begin-end")
+ | (
+ if_ + Group(LPAR + expr + RPAR) + stmtOrNull + (else_ + stmtOrNull | "")
+ ).set_name("if")
+ | (delayOrEventControl + stmtOrNull)
+ | (case + LPAR + expr + RPAR + caseItem[1, ...] + endcase)
+ | (forever + stmt)
+ | (repeat + LPAR + expr + RPAR + stmt)
+ | (while_ + LPAR + expr + RPAR + stmt)
+ | (
+ for_
+ + LPAR
+ + assgnmt
+ + SEMI
+ + Group(expr)
+ + SEMI
+ + assgnmt
+ + RPAR
+ + stmt
+ )
+ | (fork + stmt[...] + join)
+ | (fork + COLON + identifier + blockDecl[...] + stmt[...] + end)
+ | (wait + LPAR + expr + RPAR + stmtOrNull)
+ | ("->" + identifier + SEMI)
+ | (disable + identifier + SEMI)
+ | (assign + assgnmt + SEMI)
+ | (deassign + lvalue + SEMI)
+ | (force + assgnmt + SEMI)
+ | (release + lvalue + SEMI)
+ | (begin + COLON + identifier + blockDecl[...] + stmt[...] + end).set_name(
+ "begin:label-end"
+ )
+ |
+ # these *have* to go at the end of the list!!!
+ (assgnmt + SEMI)
+ | (nbAssgnmt + SEMI)
+ | (
+ Combine(Optional("$") + identifier)
+ + (LPAR + DelimitedList(expr | empty) + RPAR | "")
+ + SEMI
+ )
+ ).set_name("stmtBody")
+ """
+ x::=<blocking_assignment> ;
+ x||= <non_blocking_assignment> ;
+ x||= if ( <expression> ) <statement_or_null>
+ x||= if ( <expression> ) <statement_or_null> else <statement_or_null>
+ x||= case ( <expression> ) <case_item>+ endcase
+ x||= casez ( <expression> ) <case_item>+ endcase
+ x||= casex ( <expression> ) <case_item>+ endcase
+ x||= forever <statement>
+ x||= repeat ( <expression> ) <statement>
+ x||= while ( <expression> ) <statement>
+ x||= for ( <assignment> ; <expression> ; <assignment> ) <statement>
+ x||= <delay_or_event_control> <statement_or_null>
+ x||= wait ( <expression> ) <statement_or_null>
+ x||= -> <name_of_event> ;
+ x||= <seq_block>
+ x||= <par_block>
+ x||= <task_enable>
+ x||= <system_task_enable>
+ x||= disable <name_of_task> ;
+ x||= disable <name_of_block> ;
+ x||= assign <assignment> ;
+ x||= deassign <lvalue> ;
+ x||= force <assignment> ;
+ x||= release <lvalue> ;
+ """
+ alwaysStmt = Group(always + (eventControl | "") + stmt).set_name("alwaysStmt")
+ initialStmt = Group(initial + stmt).set_name("initialStmt")
+
+ chargeStrength = Group(LPAR + (small | medium | large) + RPAR).set_name(
+ "chargeStrength"
+ )
+
+ continuousAssign = Group(
+ assign + (driveStrength | "") + (delay | "") + DelimitedList(assgnmt) + SEMI
+ ).set_name("continuousAssign")
+
+ tfDecl = (
+ parameterDecl
+ | inputDecl
+ | outputDecl
+ | inoutDecl
+ | regDecl
+ | timeDecl
+ | integerDecl
+ | realDecl
+ )
+
+ functionDecl = Group(
+ function
+ + (range_ | "integer" | "real" | "")
+ + identifier
+ + SEMI
+ + Group(tfDecl[1, ...])
+ + Group(stmt[...])
+ + endfunction
+ )
+
+ inputOutput = input_ | output
+ netDecl1Arg = (
+ nettype
+ + (expandRange | "")
+ + (delay | "")
+ + Group(DelimitedList(~inputOutput + identifier))
+ )
+ netDecl2Arg = (
+ trireg
+ + (chargeStrength | "")
+ + (expandRange | "")
+ + (delay | "")
+ + Group(DelimitedList(~inputOutput + identifier))
+ )
+ netDecl3Arg = (
+ nettype
+ + (driveStrength | "")
+ + (expandRange | "")
+ + (delay | "")
+ + Group(DelimitedList(assgnmt))
+ )
+ netDecl1 = Group(netDecl1Arg + SEMI).set_name("netDecl1")
+ netDecl2 = Group(netDecl2Arg + SEMI).set_name("netDecl2")
+ netDecl3 = Group(netDecl3Arg + SEMI).set_name("netDecl3")
+
+ gateType = one_of(
+ "and nand or nor xor xnor buf bufif0 bufif1 "
+ "not notif0 notif1 pulldown pullup nmos rnmos "
+ "pmos rpmos cmos rcmos tran rtran tranif0 "
+ "rtranif0 tranif1 rtranif1",
+ as_keyword=True,
+ )
+ gateInstance = (
+ (Group(identifier + (range_ | "")) | "")
+ + LPAR
+ + Group(DelimitedList(expr))
+ + RPAR
+ )
+ gateDecl = Group(
+ gateType
+ + (driveStrength | "")
+ + (delay | "")
+ + DelimitedList(gateInstance)
+ + SEMI
+ )
+
+ udpInstance = Group(
+ Group(identifier + (range_ | subscrRef | ""))
+ + LPAR
+ + Group(DelimitedList(expr))
+ + RPAR
+ )
+ udpInstantiation = Group(
+ identifier
+ - (driveStrength | "")
+ + (delay | "")
+ + DelimitedList(udpInstance)
+ + SEMI
+ ).set_name("udpInstantiation")
+
+ parameterValueAssignment = Group(
+ Literal("#") + LPAR + Group(DelimitedList(expr)) + RPAR
+ )
+ namedPortConnection = Group(DOT + identifier + LPAR + expr + RPAR).set_name(
+ "namedPortConnection"
+ ) # .setDebug()
+ # assert r".\abc (abc )" == namedPortConnection
+ modulePortConnection = expr | empty
+ inst_args = Group(
+ LPAR
+ + (DelimitedList(namedPortConnection) | DelimitedList(modulePortConnection))
+ + RPAR
+ ).set_name("inst_args")
+ moduleInstance = Group(Group(identifier + (range_ | "")) + inst_args).set_name(
+ "moduleInstance"
+ ) # .setDebug()
+
+ moduleInstantiation = Group(
+ identifier
+ + (parameterValueAssignment | "")
+ + DelimitedList(moduleInstance).set_name("moduleInstanceList")
+ + SEMI
+ ).set_name("moduleInstantiation")
+
+ parameterOverride = Group("defparam" + DelimitedList(paramAssgnmt) + SEMI)
+ task = Group(task + identifier + SEMI + tfDecl[...] + stmtOrNull + endtask)
+
+ specparamDecl = Group("specparam" + DelimitedList(paramAssgnmt) + SEMI)
+
+ pathDescr1 = Group(LPAR + subscrIdentifier + "=>" + subscrIdentifier + RPAR)
+ pathDescr2 = Group(
+ LPAR
+ + Group(DelimitedList(subscrIdentifier))
+ + "*>"
+ + Group(DelimitedList(subscrIdentifier))
+ + RPAR
+ )
+ pathDescr3 = Group(
+ LPAR
+ + Group(DelimitedList(subscrIdentifier))
+ + "=>"
+ + Group(DelimitedList(subscrIdentifier))
+ + RPAR
+ )
+ pathDelayValue = Group(
+ (LPAR + Group(DelimitedList(mintypmaxExpr | expr)) + RPAR)
+ | mintypmaxExpr
+ | expr
+ )
+ pathDecl = Group(
+ (pathDescr1 | pathDescr2 | pathDescr3) + EQ + pathDelayValue + SEMI
+ ).set_name("pathDecl")
+
+ portConditionExpr = Forward()
+ portConditionTerm = (unop | "") + subscrIdentifier
+ portConditionExpr <<= portConditionTerm + (binop + portConditionExpr | "")
+ polarityOp = one_of("+ -")
+ levelSensitivePathDecl1 = Group(
+ if_
+ + Group(LPAR + portConditionExpr + RPAR)
+ + subscrIdentifier
+ + (polarityOp | "")
+ + "=>"
+ + subscrIdentifier
+ + EQ
+ + pathDelayValue
+ + SEMI
+ )
+ levelSensitivePathDecl2 = Group(
+ if_
+ + Group(LPAR + portConditionExpr + RPAR)
+ + LPAR
+ + Group(DelimitedList(subscrIdentifier))
+ + (polarityOp | "")
+ + "*>"
+ + Group(DelimitedList(subscrIdentifier))
+ + RPAR
+ + EQ
+ + pathDelayValue
+ + SEMI
+ )
+ levelSensitivePathDecl = levelSensitivePathDecl1 | levelSensitivePathDecl2
+
+ edgeIdentifier = posedge | negedge
+ edgeSensitivePathDecl1 = Group(
+ (if_ + Group(LPAR + expr + RPAR) | "")
+ + LPAR
+ + (edgeIdentifier | "")
+ + subscrIdentifier
+ + "=>"
+ + LPAR
+ + subscrIdentifier
+ + (polarityOp | "")
+ + COLON
+ + expr
+ + RPAR
+ + RPAR
+ + EQ
+ + pathDelayValue
+ + SEMI
+ )
+ edgeSensitivePathDecl2 = Group(
+ (if_ + Group(LPAR + expr + RPAR) | "")
+ + LPAR
+ + (edgeIdentifier | "")
+ + subscrIdentifier
+ + "*>"
+ + LPAR
+ + DelimitedList(subscrIdentifier)
+ + (polarityOp | "")
+ + COLON
+ + expr
+ + RPAR
+ + RPAR
+ + EQ
+ + pathDelayValue
+ + SEMI
+ )
+ edgeSensitivePathDecl = edgeSensitivePathDecl1 | edgeSensitivePathDecl2
+
+ edgeDescr = one_of("01 10 0x x1 1x x0").set_name("edgeDescr")
+
+ timCheckEventControl = Group(
+ posedge | negedge | (edge + LBRACK + DelimitedList(edgeDescr) + RBRACK)
+ )
+ timCheckCond = Forward()
+ timCondBinop = one_of("== === != !==")
+ timCheckCondTerm = (expr + timCondBinop + scalarConst) | (Optional("~") + expr)
+ timCheckCond <<= (LPAR + timCheckCond + RPAR) | timCheckCondTerm
+ timCheckEvent = Group(
+ (timCheckEventControl | "") + subscrIdentifier + ("&&&" + timCheckCond | "")
+ )
+ timCheckLimit = expr
+ controlledTimingCheckEvent = Group(
+ timCheckEventControl + subscrIdentifier + ("&&&" + timCheckCond | "")
+ )
+ notifyRegister = identifier
+
+ systemTimingCheck1 = Group(
+ "$setup"
+ + LPAR
+ + timCheckEvent
+ + COMMA
+ + timCheckEvent
+ + COMMA
+ + timCheckLimit
+ + (COMMA + notifyRegister | "")
+ + RPAR
+ + SEMI
+ )
+ systemTimingCheck2 = Group(
+ "$hold"
+ + LPAR
+ + timCheckEvent
+ + COMMA
+ + timCheckEvent
+ + COMMA
+ + timCheckLimit
+ + (COMMA + notifyRegister | "")
+ + RPAR
+ + SEMI
+ )
+ systemTimingCheck3 = Group(
+ "$period"
+ + LPAR
+ + controlledTimingCheckEvent
+ + COMMA
+ + timCheckLimit
+ + (COMMA + notifyRegister | "")
+ + RPAR
+ + SEMI
+ )
+ systemTimingCheck4 = Group(
+ "$width"
+ + LPAR
+ + controlledTimingCheckEvent
+ + COMMA
+ + timCheckLimit
+ + (COMMA + expr + COMMA + notifyRegister | "")
+ + RPAR
+ + SEMI
+ )
+ systemTimingCheck5 = Group(
+ "$skew"
+ + LPAR
+ + timCheckEvent
+ + COMMA
+ + timCheckEvent
+ + COMMA
+ + timCheckLimit
+ + (COMMA + notifyRegister | "")
+ + RPAR
+ + SEMI
+ )
+ systemTimingCheck6 = Group(
+ "$recovery"
+ + LPAR
+ + controlledTimingCheckEvent
+ + COMMA
+ + timCheckEvent
+ + COMMA
+ + timCheckLimit
+ + (COMMA + notifyRegister | "")
+ + RPAR
+ + SEMI
+ )
+ systemTimingCheck7 = Group(
+ "$setuphold"
+ + LPAR
+ + timCheckEvent
+ + COMMA
+ + timCheckEvent
+ + COMMA
+ + timCheckLimit
+ + COMMA
+ + timCheckLimit
+ + (COMMA + notifyRegister | "")
+ + RPAR
+ + SEMI
+ )
+ systemTimingCheck = (
+ FollowedBy("$")
+ + (
+ systemTimingCheck1
+ | systemTimingCheck2
+ | systemTimingCheck3
+ | systemTimingCheck4
+ | systemTimingCheck5
+ | systemTimingCheck6
+ | systemTimingCheck7
+ )
+ ).set_name("systemTimingCheck")
+ sdpd = (
+ if_
+ + Group(LPAR + expr + RPAR)
+ + (pathDescr1 | pathDescr2)
+ + EQ
+ + pathDelayValue
+ + SEMI
+ )
+
+ specifyItem = (
+ specparamDecl
+ | pathDecl
+ | levelSensitivePathDecl
+ | edgeSensitivePathDecl
+ | systemTimingCheck
+ | sdpd
+ )
+ """
+ x::= <specparam_declaration>
+ x||= <path_declaration>
+ x||= <level_sensitive_path_declaration>
+ x||= <edge_sensitive_path_declaration>
+ x||= <system_timing_check>
+ x||= <sdpd>
+ """
+ specifyBlock = Group(
+ specify + specifyItem[...:endspecify] + endspecify
+ ).set_name("specifyBlock")
+
+ moduleItem = (
+ parameterDecl
+ | inputDecl
+ | outputDecl
+ | inoutDecl
+ | regDecl
+ | netDecl3
+ | netDecl1
+ | netDecl2
+ | timeDecl
+ | integerDecl
+ | realDecl
+ | eventDecl
+ | gateDecl
+ | parameterOverride
+ | continuousAssign
+ | specifyBlock
+ | initialStmt
+ | alwaysStmt
+ | task
+ | functionDecl
+ # these have to be at the end - they start with identifiers
+ | moduleInstantiation
+ | udpInstantiation
+ )
+ """ All possible moduleItems, from Verilog grammar spec
+ x::= <parameter_declaration>
+ x||= <input_declaration>
+ x||= <output_declaration>
+ x||= <inout_declaration>
+ ?||= <net_declaration> (spec does not seem consistent for this item)
+ x||= <reg_declaration>
+ x||= <time_declaration>
+ x||= <integer_declaration>
+ x||= <real_declaration>
+ x||= <event_declaration>
+ x||= <gate_declaration>
+ x||= <UDP_instantiation>
+ x||= <module_instantiation>
+ x||= <parameter_override>
+ x||= <continuous_assign>
+ x||= <specify_block>
+ x||= <initial_statement>
+ x||= <always_statement>
+ x||= <task>
+ x||= <function>
+ """
+ portRef = subscrIdentifier
+ portExpr = portRef | Group(LBRACE + DelimitedList(portRef) + RBRACE)
+ port = portExpr | Group(DOT + identifier + LPAR + portExpr + RPAR)
+
+ moduleHdr = Group(
+ (module | macromodule)
+ + identifier
+ + (
+ LPAR
+ + Group(
+ (
+ DelimitedList(
+ Group(
+ (input_ | output)
+ + (netDecl1Arg | netDecl2Arg | netDecl3Arg)
+ )
+ | port
+ )
+ | ""
+ )
+ )
+ + RPAR
+ | ""
+ )
+ + SEMI
+ ).set_name("moduleHdr")
+
+ module_expr = Group(
+ moduleHdr + Group(moduleItem[...:endmodule]) + endmodule
+ ).set_name(
+ "module"
+ ) # .setDebug()
+
+ udpDecl = outputDecl | inputDecl | regDecl
+ # udpInitVal = one_of("1'b0 1'b1 1'bx 1'bX 1'B0 1'B1 1'Bx 1'BX 1 0 x X")
+ udpInitVal = (Regex("1'[bB][01xX]|[01xX]")).set_name("udpInitVal")
+ udpInitialStmt = Group(
+ "initial" + identifier + EQ + udpInitVal + SEMI
+ ).set_name("udpInitialStmt")
+
+ levelSymbol = one_of("0 1 x X ? b B")
+ levelInputList = Group(levelSymbol[1, ...].set_name("levelInpList"))
+
+ outputSymbol = one_of("0 1 x X")
+ combEntry = Group(levelInputList + COLON + outputSymbol + SEMI)
+ edgeSymbol = one_of("r R f F p P n N *")
+ edge = Group(LPAR + levelSymbol + levelSymbol + RPAR) | Group(edgeSymbol)
+ edgeInputList = Group(levelSymbol[...] + edge + levelSymbol[...])
+ inputList = levelInputList | edgeInputList
+ seqEntry = Group(
+ inputList + COLON + levelSymbol + COLON + (outputSymbol | "-") + SEMI
+ ).set_name("seqEntry")
+ udpTableDefn = Group(
+ table + (combEntry | seqEntry)[1, ...] + endtable
+ ).set_name("table")
+
+ """
+ <UDP>
+ ::= primitive <name_of_UDP> ( <name_of_variable> <,<name_of_variable>>* ) ;
+ <UDP_declaration>+
+ <UDP_initial_statement>?
+ <table_definition>
+ endprimitive
+ """
+ udp = Group(
+ primitive
+ + identifier
+ + LPAR
+ + Group(DelimitedList(identifier))
+ + RPAR
+ + SEMI
+ + udpDecl[1, ...]
+ + (udpInitialStmt | "")
+ + udpTableDefn
+ + endprimitive
+ )
+
+ verilogbnf = (module_expr | udp)[1, ...] + StringEnd()
+
+ verilogbnf.ignore(cppStyleComment)
+ verilogbnf.ignore(compilerDirective)
+
+ return verilogbnf
+
+
+def test(strng):
+ tokens = []
+ try:
+ tokens = make_verilog_bnf().parse_string(strng)
+ except ParseException as err:
+ print(err.explain())
+ return tokens
+
+
+if __name__ == "__main__":
+
+ def main():
+ import sys
+
+ sys.setrecursionlimit(5000)
+ print(f"Verilog parser test (V {__version__})")
+ print(f" - using pyparsing version {pyparsing.__version__}")
+ print(f" - using Python version {sys.version}")
+ if packratOn:
+ print(" - using packrat parsing")
+ print()
+
+ import gc
+
+ failCount = 0
+ make_verilog_bnf()
+ numlines = 0
+ fileDir = "verilog"
+ fileDir = "scratch/verilog"
+ # fileDir = "scratch/verilog/new3"
+ fileDir = Path(fileDir)
+ allFiles = [f for f in fileDir.glob("*.v")]
+
+ pretty = pprint.PrettyPrinter(indent=2)
+ totalTime = 0
+ for vfile in allFiles:
+ gc.collect()
+ gc.collect()
+ filelines = vfile.read_text().splitlines()
+ print(vfile.name, len(filelines), end=" ")
+ numlines += len(filelines)
+ teststr = "\n".join(filelines)
+ time1 = time.perf_counter()
+ tokens = test(teststr)
+ time2 = time.perf_counter()
+ elapsed = time2 - time1
+ totalTime += elapsed
+ if len(tokens):
+ print(f"OK {elapsed}")
+
+ (fileDir / "parseOutput").mkdir(exist_ok=True)
+ outfile = fileDir / "parseOutput" / (vfile.name + ".parsed.txt")
+ outfile.write_text(f"{teststr}\n\n{pretty.pformat(tokens.as_list())}\n")
+ else:
+ print(f"failed {elapsed}")
+ failCount += 1
+ for i, line in enumerate(filelines, 1):
+ print(f"{i:4d}: {line.rstrip()}")
+
+ print(f"Total parse time: {totalTime}")
+ print(f"Total source lines: {numlines}")
+ print(f"Average lines/sec: {numlines / (totalTime + 0.05):.1f}")
+ if failCount:
+ print(f"FAIL - {failCount} files failed to parse")
+ else:
+ print("SUCCESS - all files parsed")
+
+ return 0
+
+ main()