Commit 9973916e authored by Mikhail Karpenko's avatar Mikhail Karpenko
Browse files

Add Quartus parser script

parent 080053fa
Loading
Loading
Loading
Loading
+193 −0
Original line number Diff line number Diff line
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2015, Elphel, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#  
#  Additional permission under GNU GPL version 3 section 7:
#  If you modify this Program, or any covered work, by linking or combining it
#  with Eclipse or Eclipse plugins (or a modified version of those libraries),
#  containing parts covered by the terms of EPL/CPL, the licensors of this
#  Program grant you additional permission to convey the resulting work.
#  {Corresponding Source for a non-source form of such a combination shall
#  include the source code for the parts of Eclipse or Eclipse plugins used
#  as well as that of the covered work.}

import sys
import argparse
import re

# Global variables
g_topModule = ""
g_toolName = ""
g_mode = ""
g_includeMsgId = True
# Lines starting with these markers are of lines of interest
g_msgTypeMarkers = ("Info", "Warning", "Error", "Critical Warning", "Extra Info")
# Command line paramerters, these names should be in sync with those that are passed to the script in XML files (quartus_proto.xml probably)
g_optNames = ["top_module", "tool_name", "parser_mode", "include_msg_id"]

# Search patterns
patternMsgType = re.compile(".*?:")
patternMsgId = re.compile("[0-9]+")
patternFileLine = re.compile("([^ ]+)[ ]?\(([0-9]+)")
patternFileNameFull = re.compile("File:(.*)Line:")

class MessageContainer:
	"""Helper container for parsed message line"""
	msgType = ""
	msgId = 0
	msgText = ""
	fileName = ""
	fileNameFull = ""
	lineNumber = 0

	#Private parameters
	__discardId = True

	def discardId(self, value = None):
		if value == None:
			return self.__discardId
		else:
			self.__discardId = value

def getParameters():
	"""Extract parameters from command line arguments"""
	global g_topModule
	global g_toolName
	global g_mode
	global g_includeMsgId

	parser = argparse.ArgumentParser()
	for opt in g_optNames:
		parser.add_argument("--{}".format(opt))
	args = parser.parse_args()
	if args.top_module:
		g_topModule = args.top_module
	if args.tool_name:
		g_toolName = args.tool_name
	if args.parser_mode:
		g_mode = args.parser_mode
	if args.include_msg_id:
		if args.include_msg_id == "true":
			g_includeMsgId = True
		else:
			g_includeMsgId = False

def isProblem(line):
	"""Check if the line contains meaningful information"""
	global g_msgTypeMarkers
	retVal = False
	strippedLine = line.strip()
	for msgType in g_msgTypeMarkers:
		if strippedLine.startswith(msgType):
			retVal = True
			break
	return retVal

def getMsgId(line, msg):
	"""Extract message ID from log line an set coresponding msg field
	line - text string to be parsed
	msg - instance of MessageContainer class
	return: instance of MessageContainer class
	"""
	matchedText = patternMsgId.search(line)
	if matchedText:
		msgId = line[matchedText.start() : matchedText.end()]
		msg.msgId = int(msgId)
	return msg

def getMsgType(line, msg):
	"""Extract message type from log line an set coresponding msg field
	line - text string to be parsed
	msg - instance of MessageContainer class
	return: log line without message type and instance of MessageContainer class
	"""
	strippedLine = line.strip()
	matchedText = patternMsgType.search(strippedLine)
	if matchedText:
		msgType = strippedLine[matchedText.start() : matchedText.end() - 1]
		msg = getMsgId(msgType, msg)
		if msg.msgId != 0:
			#remove message ID
			msg.msgType = msgType.split('(')[0].strip()
			msg.discardId(False)
		else:
			msg.msgType = msgType
	return (strippedLine[matchedText.end() + 1:], msg)

def getFileLine(line, msg):
	"""Extract file name and line number from log line an set coresponding msg fields
	line - text string to be parsed
	msg - instance of MessageContainer class
	return: log line without extracted information
	"""
	matchedLine = patternFileLine.search(line)
	if matchedLine and len(matchedLine.groups()) == 2:
		msg.lineNumber = int(matchedLine.group(2))
		msg.fileName = matchedLine.group(1)
		#remove part of the string preceding file name as well as trailing braket and colon
		#line = line[matchedLine.end(2) + 2:]
	return (line, msg)

def getFilePath(line, msg):
	"""Extract full file name and line number from log line an set coresponding msg fields
	line - text string to be parsed
	msg - instance of MessageContainer class
	return: log line without extracted information
	"""
	matchedLine = patternFileNameFull.search(line)
	if matchedLine:
		msg.fileNameFull = matchedLine.group(1).strip()
		line = line[:matchedLine.start() - 1]
	return (line, msg)

def getMsgText(line, msg):
	msg.msgText = line.strip()
	return ("", msg)

def filterMessage(msg):
	"""Decide whether this message should be redirected to output or filtered out"""
	#just a stub
	return True

def assembleLine(msg):
	"""Assemble and return output line"""
	if not g_includeMsgId or msg.discardId():
		formatStr = "{0}: {2} [{3}:{4:04d}]\n"
	else:
		formatStr = "{0}: [{1}] {2} [{3}:{4:04d}]\n"
	if msg.fileName =="":
		problemMarker = g_toolName
	else:
		problemMarker = msg.fileName
	line = formatStr.format(msg.msgType,
			msg.msgId,
			msg.msgText,
			problemMarker,
			msg.lineNumber
			)
	return line

if __name__ == "__main__":
	getParameters()
	for line in iter(sys.stdin.readline, ''):
		if isProblem(line):
			msg = MessageContainer()
			processedLine, msg = getMsgType(line, msg)
			processedLine, msg = getFilePath(processedLine, msg)
			processedLine, msg = getFileLine(processedLine, msg)
			processedLine, msg = getMsgText(processedLine, msg)
			if filterMessage(msg):
				logLine = assembleLine(msg)
				sys.stdout.write(logLine)
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
	<interface name="QuartusInterface" extends="FPGAPprojectInterface">
		<syntax name="GrepFilterProblemSyntax"        format='| grep --line-buffered -v  "\[%%ParamName"' />
		<syntax name="GrepFilterProblemOtherSyntax"   format='%(| grep --line-buffered -v  "\[%%ParamValue"%|\n%)' />
		<syntax name="CmdLineParameterSyntax"         format="--%%ParamName %%ParamValue"/>
	    <typedef name="ParserModeType">
      		<paramtype kind= "enum" base="String">
      			<item value="0"   label="Tool output is parsed immediately"/>
+84 −0
Original line number Diff line number Diff line
@@ -35,5 +35,89 @@
		description="Quartus tool prototype"
		abstract="true"
		>
		<!-- parser parameters -->
        <parameter id="PatternErrors" label="Errors" tooltip="Regular expression for error messages"
			default=".*(Error:)(.*)\[(.*):([0-9]+)\]"
			visible="true" type="String" format="CopyValue"/>
		<parameter id="PatternWarnings" label="Warnings" tooltip="Regular expression for warnings messages"
			default=".*(Warning:|Critical Warning:)(.*)\[(.*):([0-9]+)\]"
			visible="true" type="String" format="CopyValue"/>
		<parameter id="PatternInfo" label="Info" tooltip="Regular expression for info messages"
			default=".*(Info:)(.*)\[(.*):([0-9]+)\]"
			visible="true" type="String" format="CopyValue"/>
		<parameter id="InstanceCapture" label="Instance capture"
			tooltip="Regular expression to extract hierarchical instance name (outer '()' enclose the hierarchical name itself)"
			default="((([#a-zA-Z_$]([a-zA-Z_$0-9]|\[[0-9:]+\])*)(\.|:))+([a-zA-Z_$]([a-zA-Z_$0-9]|\[[0-9:]+\])*))"
			visible="true" type="String" format="CopyValue"/>
		<parameter id="InstanceSeparator" label="Instance separator"
			tooltip="Regular expression to extract hierarchical name separator, for literal '.' it is '\.'"
			default="\."
			visible="true" type="String" format="CopyValue"/>
		<parameter id="InstanceSuffix" label="Instance suffix"
			tooltip="Regular expression to match/remove name suffixes automatically added by the tools"
			default="_reg|__parameterized[0-9]*"
			visible="true" type="String" format="CopyValue"/>
		<parameter id="ShowWarnings" label="Show Warnings" tooltip="Parse warning messages"
			default="true" type="Boolean" format="None"/>
		<parameter id="ShowInfo" label="Show info" tooltip="Parse info messages"
			default="true" type="Boolean" format="None"/>
		<parameter id="PreGrepW" visible="false"
			type="String" format="None" default="?%ShowWarnings=true: |Warning, "/>
		<parameter id="PreGrepI" visible="false"
			type="String" format="None"	default="?%ShowInfo=true: |Info, "/>
		<parameter id="GrepEWI" label="Grep filter" tooltip="Calculated grep filter"
			default="grep --line-buffered -E 'Error|CRITICAL WARNING%PreGrepW%PreGrepI'"
			type="String" format="CopyValue" visible="true" readonly="true"/>
		<parameter id="MaxMsg" outid="set_param messaging.defaultLimit"	label="Maximal messages" tooltip="Maximum number of messages to output (per type)"
			default="1000" visible="true" omit="100" type="Cardinal" format="NameValue"/>

		<parameter id="parser_mode" label="Parser mode" tooltip="Parser mode (consolidation of bits of the same instance)"
			default="1" visible="true" omit="" type="ParserModeType" format="CmdLineParameterSyntax"/>
		<parameter id="parsers_path" label="Parsers Path" tooltip="parsers directory in plugins"
			default="%%ParsersPath" visible="true" omit="" type="String" format="CopyValue"/>
		<parameter id="parser_name" label="Quartus parser name" tooltip="Quartus output parser script path"
			default="parser_quartus.py" visible="true" omit="" type="String" format="CopyValue"/>

		<parameter id="include_msg_id" label="Inlcude message ID" tooltip="Include message ID to output"
			type="Boolean" default="true" visible="true" readonly="false" format="CmdLineParameterSyntax"/>

		<!-- Invisible parameters-->
		<parameter id="tool_name" type="String" default="%%ToolName" visible="false" format="CmdLineParameterSyntax"/>
		<parameter id="top_module" type="String" default="%%TopModule" visible="false" format="CmdLineParameterSyntax"/>

		<input>
			<group name="Parser" weight="10">
				"parsers_path"
				"parser_name"
				"---"
				"ShowWarnings"
				"ShowInfo"
				"MaxMsg"
				"GrepEWI"
				"---"
				"include_msg_id"
				"---"
				"PatternErrors"
				"PatternWarnings"
 				"PatternInfo"
 				"InstanceCapture"
 				"InstanceSeparator"
 				"InstanceSuffix"
 				"parser_mode"
			</group>
		</input>
		<output>
			<line name="parser_Quartus"
				errors="PatternErrors"
				warnings="PatternWarnings"
				info="PatternInfo"
				instance-capture="InstanceCapture"
 				instance-separator="InstanceSeparator"
 				instance-suffix="InstanceSuffix">
				"-c"
				"python -u %parsers_path%parser_name %tool_name %top_module %parser_mode %include_msg_id | "
				"%GrepEWI"
			</line>
		</output>
	</tool>
</vdt-project>
+3 −1
Original line number Diff line number Diff line
@@ -212,7 +212,7 @@ semantec errors, and perform a netlist exraction."
				<if QuartusSynthActionIndex="1">
					"%analysis_and_elaboration"
				</if>
				"\"\n"
				"\""
			</line>
			<line name="quartus_run_synth"
			      dest="QuartusConsole"
@@ -235,6 +235,8 @@ semantec errors, and perform a netlist exraction."
				"project_open $projectName"
				"} else {"
				"project_new $projectName }"
				"set_global_assignment -name VERILOG_FILE %ImplementationTopFile"
				"set_global_assignment -name TOP_LEVEL_ENTITY %ImplementationTopModule"
				"foreach file [list %FilteredSourceListPar] {"
				"puts \"Adding $file to project\""
				"set_global_assignment -name VERILOG_FILE $file }"
+3 −1
Original line number Diff line number Diff line
@@ -245,6 +245,8 @@
			type="String" default="" format="CopyValue" readonly="false" />
		<parameter id="ImplementationTopFile" label="Implementation top file" tooltip="Project file with top implementation module"
			type="Filename" default="" format="CopyValue" readonly="false" />
		<parameter id="ImplementationTopModule" label="Implementation top module" tooltip="Project top implementation module"
			type="String" default="" format="CopyValue" readonly="false" />
		<parameter id="SimulDir" label="Simulation directory" tooltip="Project simulation directory"
			type="Pathname" default="simulation" format="CopyValue" readonly="false" />
		<!--  Vivado parameters  -->
@@ -385,7 +387,6 @@
			label="Disable Vivado bitstream" tooltip="Disable tool Vivado bitstream generator"
			default="false"
			type="Boolean" format="None" />
 				"" <!--  same as in project -->

		<!-- Invisible (calculated) project-wide parameters -->			
		<parameter id="SimulDirSlash" type="Pathname" visible="false"
@@ -475,6 +476,7 @@
			<group name="General" label="General parameters">
			    "part"
				"ImplementationTopFile"
				"ImplementationTopModule"
			</group>
			<group name="Simulation" label="Simulation properties">
				"SimulationTopFile"