blob: 708faf3a3e6b0ffa5fbb77a87867c3598ac66f4c [file] [log] [blame]
#!/usr/bin/env python2
import sys
import os
import errno
import argparse
import subprocess
import time
import shutil
import re
import textwrap
from datetime import datetime
from collections import OrderedDict
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'python_libs'))
from verilogtorouting.flow import run_vtr_flow, parse_vtr_flow, VTR_STAGE, vtr_stages, CommandRunner
from verilogtorouting.error import *
from verilogtorouting.util import print_verbose, RawDefaultHelpFormatter, VERBOSITY_CHOICES, find_vtr_file, format_elapsed_time
BASIC_VERBOSITY = 1
class VtrStageArgparseAction(argparse.Action):
def __call__(self, parser, namespace, value, option_string=None):
if value == "odin":
setattr(namespace, self.dest, VTR_STAGE.odin)
elif value == "abc":
setattr(namespace, self.dest, VTR_STAGE.abc)
elif value == "vpr":
setattr(namespace, self.dest, VTR_STAGE.vpr)
elif value == "lec":
setattr(namespace, self.dest, VTR_STAGE.lec)
else:
raise argparse.ArgumentError(self, "Invalid VTR stage '" + value + "'")
class OnOffArgparseAction(argparse.Action):
def __call__(self, parser, namespace, value, option_string=None):
if value.lower() == "on":
setattr(namespace, self.dest, True)
elif value.lower() == "off":
setattr(namespace, self.dest, False)
else:
raise argparse.ArgumentError(self, "Invalid yes/no value '" + value + "'")
on_off_choices = ['on', 'off']
def vtr_command_argparser(prog=None):
usage = "%(prog)s architecture_file circuit_file [options]"
description = textwrap.dedent(
"""
Runs a single architecture and circuit through the VTR CAD flow - mapping
the circuit onto the target archietcture.
Any unrecognzied arguments are passed to VPR.
"""
)
epilog = textwrap.dedent(
"""
Examples
--------
Produce more output (e.g. individual tool outputs):
%(prog)s arch.xml circuit.v -v 2
Route at a fixed channel width:
%(prog)s arch.xml circuit.v --route_chan_width 100
Passing '--route_chan_width 100' will force VPR to route at a fixed channel width,
rather then attempting to find the minimum channel width (VPR's default behaviour).
Run in the directory 'test' (rather than in a temporary directory):
%(prog)s arch.xml circuit.v --work_dir test
Enable power analysis:
%(prog)s arch.xml circuit.v --power_tech power_tech.xml
Only run a specific stage (assumes required results have already been generated):
%(prog)s arch.xml circuit.blif --start vpr --end vpr
"""
)
parser = argparse.ArgumentParser(
prog=prog,
usage=usage,
description=description,
epilog=epilog,
formatter_class=RawDefaultHelpFormatter,
)
#
# Major arguments
#
parser.add_argument('architecture_file',
help="The FPGA architecture to target.")
parser.add_argument('circuit_file',
help="The circuit to map to the target architecture.")
parser.add_argument("--start", "--starting_stage",
choices=VTR_STAGE.reverse_mapping.values(),
default=VTR_STAGE.odin,
action=VtrStageArgparseAction,
help="Starting stage of the VTR flow.")
parser.add_argument("--end", "--ending_stage",
choices=VTR_STAGE.reverse_mapping.values(),
default=VTR_STAGE.vpr,
action=VtrStageArgparseAction,
help="Ending stage of the VTR flow.")
parser.add_argument("-v", "--verbosity",
choices=VERBOSITY_CHOICES,
default=2,
type=int,
help="Verbosity of the script. Higher values produce more output.")
parser.add_argument("--parse_config_file",
default=None,
help="Parse file to run after flow completion")
parser.add_argument("--parse",
default=False,
action="store_true",
dest="parse_only",
help="Perform only parsing (assumes a previous run exists in work_dir)")
#
# Power arguments
#
power = parser.add_argument_group("Power", description="Power Analysis Related Options")
power.add_argument("--power",
default=None,
dest="power_tech",
metavar="POWER_TECH_FILE",
help="Enables power analysis using the specified technology file. Power analysis is disabled if not specified.")
#
# House keeping arguments
#
house_keeping = parser.add_argument_group("House Keeping", description="Configuration related to how files/time/memory are used by the script.")
house_keeping.add_argument("--work_dir",
default=".",
help="Directory to run the flow in (will be created if non-existant).")
house_keeping.add_argument("--track_memory_usage",
choices=on_off_choices,
default="on",
action=OnOffArgparseAction,
help="Track the memory usage for each stage. Requires /usr/bin/time -v, disabled if not available.")
house_keeping.add_argument("--memory_limit",
default=None,
metavar="MAX_MEMORY_MB",
help="Specifies the maximum memory usable by any stage. "
"Not supported on some platforms (requires ulimit).")
house_keeping.add_argument("--timeout",
default=14*24*60*60, #14 days
type=float,
metavar="TIMEOUT_SECONDS",
help="Maximum time in seconds to spend on a single stage.")
return parser
def main():
vtr_command_main(sys.argv[1:])
def vtr_command_main(arg_list, prog=None):
start = datetime.now()
#Load the arguments
args, unkown_args = vtr_command_argparser(prog).parse_known_args(arg_list)
print_verbose(BASIC_VERBOSITY, args.verbosity, "# {} {}\n".format(prog, ' '.join(arg_list)))
abs_path_arch_file = os.path.abspath(args.architecture_file)
abs_path_circuit_file = os.path.abspath(args.circuit_file)
abs_work_dir = os.path.abspath(args.work_dir)
#Specify how command should be run
command_runner = CommandRunner(track_memory=args.track_memory_usage,
max_memory_mb=args.memory_limit,
timeout_sec=args.timeout,
verbose=True if args.verbosity >= 2 else False,
echo_cmd=True if args.verbosity >= 4 else False)
exit_status = 0
try:
if not args.parse_only:
try:
vpr_args = process_unkown_args(unkown_args)
#Run the flow
run_vtr_flow(abs_path_arch_file,
abs_path_circuit_file,
power_tech_file=args.power_tech,
work_dir=abs_work_dir,
start_stage=args.start,
end_stage=args.end,
command_runner=command_runner,
verbosity=args.verbosity,
vpr_args=vpr_args
)
except CommandError as e:
#An external command failed
print "Error: {msg}".format(msg=e.msg)
print "\tfull command: ", ' '.join(e.cmd)
print "\treturncode : ", e.returncode
print "\tlog file : ", e.log
exit_status = 1
except InspectError as e:
#Something went wrong gathering information
print "Error: {msg}".format(msg=e.msg)
print "\tfile : ", e.filename
exit_status = 2
except VtrError as e:
#Generic VTR errors
print "Error: ", e.msg
exit_status = 3
except KeyboardInterrupt as e:
print "{} recieved keyboard interrupt".format(prog)
exit_status = 4
#Parse the flow results
try:
parse_vtr_flow(abs_work_dir, args.parse_config_file, verbosity=args.verbosity)
except InspectError as e:
print "Error: {msg}".format(msg=e.msg)
print "\tfile : ", e.filename
exit_status = 2
finally:
print_verbose(BASIC_VERBOSITY, args.verbosity, "\n{} took {}".format(prog, format_elapsed_time(datetime.now() - start)))
sys.exit(exit_status)
def process_unkown_args(unkown_args):
#We convert the unkown_args into a dictionary, which is eventually
#used to generate arguments for VPR
vpr_args = OrderedDict()
while len(unkown_args) > 0:
#Get the first argument
arg = unkown_args.pop(0)
if arg == '':
continue
if not arg.startswith('-'):
raise VtrError("Extra argument '{}' intended for VPR does not start with '-'".format(arg))
#To make it a valid kwargs dictionary we trim the initial '-' or '--' from the
#argument name
assert len(arg) >= 2
if arg[1] == '-':
#Double-dash prefix
arg = arg[2:]
else:
#Single-dash prefix
arg = arg[1:]
#Determine if there is a value associated with this argument
if len(unkown_args) == 0 or unkown_args[0].startswith('-'):
#Single value argument, we place these with value 'True'
#in vpr_args
vpr_args[arg] = True
else:
#Multivalue argument
val = unkown_args.pop(0)
vpr_args[arg] = val
return vpr_args
if __name__ == "__main__":
retval = main()
sys.exit(retval)