From d0641bdf5348be8a4617b76d920da59133b9d1f3 Mon Sep 17 00:00:00 2001 From: Unai Martinez-Corral Date: Fri, 4 Mar 2022 01:02:11 +0100 Subject: [PATCH] f4pga/wrappers/xc7 * move helpers from common to __init__ * merge write_fasm, route and place into __init__ * rename common to vpr * synth: style * write_fasm: style Signed-off-by: Unai Martinez-Corral --- f4pga/setup.py | 7 +- f4pga/wrappers/__init__.py | 28 +++++ f4pga/wrappers/xc7/__init__.py | 113 +++++++++++++++++++++ f4pga/wrappers/xc7/common.py | 101 ------------------ f4pga/wrappers/xc7/place.py | 34 ------- f4pga/wrappers/xc7/route.py | 30 ------ f4pga/wrappers/xc7/synth.py | 169 +++++++++++++++++++------------ f4pga/wrappers/xc7/vpr.py | 133 ++++++++++++++++++++++++ f4pga/wrappers/xc7/write_fasm.py | 48 --------- 9 files changed, 383 insertions(+), 280 deletions(-) create mode 100644 f4pga/wrappers/__init__.py delete mode 100644 f4pga/wrappers/xc7/common.py delete mode 100644 f4pga/wrappers/xc7/place.py delete mode 100644 f4pga/wrappers/xc7/route.py create mode 100644 f4pga/wrappers/xc7/vpr.py delete mode 100644 f4pga/wrappers/xc7/write_fasm.py diff --git a/f4pga/setup.py b/f4pga/setup.py index d51e01f..acb8ad9 100644 --- a/f4pga/setup.py +++ b/f4pga/setup.py @@ -80,6 +80,7 @@ setuptools_setup( "f4pga", "f4pga.common_modules", "f4pga.wrappers.sh", + "f4pga.wrappers", "f4pga.wrappers.xc7" ], package_dir={"f4pga": "."}, @@ -93,10 +94,10 @@ setuptools_setup( entry_points={ "console_scripts": [ "f4pga = f4pga.__init__:main", - "f4pga-place = f4pga.wrappers.xc7.place:main", - "f4pga-route = f4pga.wrappers.xc7.route:main", + "f4pga-place = f4pga.wrappers.xc7.__init__:place", + "f4pga-route = f4pga.wrappers.xc7.__init__:route", "f4pga-synth = f4pga.wrappers.xc7.synth:main", - "f4pga-write-fasm = f4pga.wrappers.xc7.write_fasm:main", + "f4pga-write-fasm = f4pga.wrappers.xc7.__init__:write_fasm", ] + wrapper_entrypoints }, ) diff --git a/f4pga/wrappers/__init__.py b/f4pga/wrappers/__init__.py new file mode 100644 index 0000000..7c627bc --- /dev/null +++ b/f4pga/wrappers/__init__.py @@ -0,0 +1,28 @@ +from pathlib import Path +from os import environ +from sys import argv as sys_argv +from subprocess import run as subprocess_run + + +def run(*args): + """ + Execute subroutine + """ + out = subprocess_run(args, capture_output=True) + if out.returncode != 0: + raise(Exception(out.returncode)) + return out.stdout + + +def noisy_warnings(device): + """ + Emit some noisy warnings + """ + environ['OUR_NOISY_WARNINGS'] = f'noisy_warnings-{device}_pack.log' + + +def my_path(): + """ + Get current PWD + """ + return str(Path(sys_argv[0]).resolve().parent) diff --git a/f4pga/wrappers/xc7/__init__.py b/f4pga/wrappers/xc7/__init__.py index e69de29..7a9cf14 100644 --- a/f4pga/wrappers/xc7/__init__.py +++ b/f4pga/wrappers/xc7/__init__.py @@ -0,0 +1,113 @@ +from pathlib import Path +from re import search as re_search + +from f4pga.wrappers import ( + my_path, + noisy_warnings, + run +) + +from f4pga.wrappers.xc7.vpr import ( + save_vpr_log, + setup_vpr_arg_parser, + VprArgs, + vpr +) + + +def place(): + parser = setup_vpr_arg_parser() + parser.add_argument( + '-n', + '--net', + nargs='+', + metavar='', + type=str, + help='NET filename' + ) + args = parser.parse_args() + + vprargs = VprArgs(my_path(), args) + [ + '--fix_clusters', + 'constraints.place', + '--place' + ] + vprargs.export() + + if not args.net: + print('Please provide NET filename') + exit(1) + + noisy_warnings() + + print('Generating constraints...\n') + + run( + 'symbiflow_generate_constraints', + args.eblif, + args.net, + args.part, + vprargs.arch_def, + args.pcf + ) + + vpr(vprargs) + + save_vpr_log('place.log') + + +def route(): + args = setup_vpr_arg_parser().parse_args() + + vprargs = VprArgs(my_path(), args) + vprargs.export() + + noisy_warnings(args.device) + + vprargs.optional += '--route' + + print('Routing...') + vpr(vprargs) + + save_vpr_log('route.log') + + +def write_fasm(): + vprargs = VprArgs( + my_path(), + setup_vpr_arg_parser().parse_args() + ) + + if vprargs.eblif is None: + raise(Exception("Argument EBLIF is required!")) + + top_ext_match = re_search('.*\\.[^.]*', vprargs.eblif) + top = top[:top_ext_match.pos] if top_ext_match else vprargs.eblif + + fasm_extra = top + '_fasm_extra.fasm' + + noisy_warnings() + + run( + 'genfasm', + vprargs.arch_def, + vprargs.eblif, + '--device', vprargs.device_name, + vprargs.vpr_options, + '--read_rr_graph', vprargs.rr_graph + ) + + print(f'FASM extra: {fasm_extra}\n') + + # Concatenate top.fasm with extra.fasm if necessary + if Path(fasm_extra).is_file(): + print('writing final fasm') + with open(top + '.fasm', 'r+<') as top_file, open(fasm_extra) as extra_file: + cat = top_file.read() + cat += '\n' + cat += extra_file.read() + top_file.seek(0) + top_file.write(cat) + top_file.truncate() + + save_vpr_log('fasm.log') diff --git a/f4pga/wrappers/xc7/common.py b/f4pga/wrappers/xc7/common.py deleted file mode 100644 index a2306b1..0000000 --- a/f4pga/wrappers/xc7/common.py +++ /dev/null @@ -1,101 +0,0 @@ -from pathlib import Path -import subprocess -import argparse -import os -import shutil -from sys import argv as sys_argv - -class VprArgs: - arch_dir: str - arch_def: str - lookahead: str - rr_graph: str - rr_graph_xml: str - place_delay: str - device_name: str - eblif: str - vpr_options: str - optional: list[str] - - def __init__(self, mypath, args): - self.arch_dir = \ - str(Path(mypath) / '../share/symbiflow/arch' / args.device) - self.arch_dir = os.path.realpath(self.arch_dir) - self.arch_def = os.path.join(self.arch_dir, 'arch.timing.xml') - self.lookahead = \ - os.path.join(self.arch_dir, - 'rr_graph_' + args.device + '.lookahead.bin') - self.rr_graph = \ - os.path.join(self.arch_dir, - 'rr_graph_' + args.device + '.rr_graph.real.bin') - self.rr_graph_xml = \ - os.path.join(self.arch_dir, - 'rr_graph_' + args.device + '.rr_graph.real.xml') - self.place_delay = \ - os.path.join(self.arch_dir, - 'rr_graph_' + args.device + '.place_delay.bin') - self.device_name = args.device.replace('_', '-') - self.eblif = args.eblif - self.vpr_options = args.vpr_options - self.optional = [] - if args.sdc: - self.optional += ['--sdc_file', args.sdc] - - def export(self): - os.environ['ARCH_DIR'] = self.arch_dir - os.environ['ARCH_DEF'] = self.arch_def - os.environ['LOOKAHEAD'] = self.lookahead - os.environ['RR_GRAPH'] = self.rr_graph - os.environ['RR_GRAPH_XML'] = self.rr_graph_xml - os.environ['PLACE_DELAY'] = self.place_delay - os.environ['DEVICE_NAME'] = self.device_name - -def setup_vpr_arg_parser(): - parser = argparse.ArgumentParser(description="Parse flags") - parser.add_argument('-d', '--device', nargs=1, metavar='', - type=str, help='Device type (e.g. artix7)', default='artix7') - parser.add_argument('-e', '--eblif', nargs=1, metavar='', - type=str, help='EBLIF filename') - parser.add_argument('-p', '--pcf', nargs=1, metavar='', - type=str, help='PCF filename') - parser.add_argument('-P', '--part', nargs=1, metavar='', - type=str, help='Part name') - parser.add_argument('-s', '--sdc', nargs=1, metavar='', - type=str, help='SDC file') - parser.add_argument('-a', '--vpr_options', metavar='', - type=str, help='Additional VPR options') - parser.add_argument('additional_vpr_args', nargs='*', metavar='', - type=str, help='Additional arguments for vpr command') - return parser - -# Exwecute subroutine -def sub(*args): - out = subprocess.run(args, capture_output=True) - if out.returncode != 0: - exit(out.returncode) - return out.stdout - -# Execute `vpr` -def vpr(vprargs: VprArgs): - return sub('vpr', - vprargs.arch_def, - vprargs.eblif, - '--device', vprargs.device_name, - vprargs.vpr_options, - '--read_rr_graph', vprargs.rr_graph, - '--read_router_lookahead', vprargs.lookahead, - 'read_placement_delay_lookup', vprargs.place_delay, - *vprargs.optional) - -# Emit some noisy warnings -def noisy_warnings(device): - os.environ['OUR_NOISY_WARNINGS'] = 'noisy_warnings-' + device + '_pack.log' - -# Get current PWD -def my_path(): - mypath = os.path.realpath(sys_argv[0]) - return os.path.dirname(mypath) - -# Save VPR log -def save_vpr_log(filename): - shutil.move('vpr_stdout.log', filename) diff --git a/f4pga/wrappers/xc7/place.py b/f4pga/wrappers/xc7/place.py deleted file mode 100644 index c253b1a..0000000 --- a/f4pga/wrappers/xc7/place.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/python3 - -import shutil -from f4pga.wrappers.xc7.common import ( - my_path, - setup_vpr_arg_parser, - VprArgs, - vpr -) - -def main(): - mypath = my_path() - parser = setup_vpr_arg_parser() - parser.add_argument('-n', '--net', nargs='+', metavar='', - type=str, help='NET filename') - args = parser.parse_args() - vprargs = VprArgs(mypath, args) - vprargs += ['--fix_clusters', 'constraints.place', '--place'] - vprargs.export() - - if not args.net: - print('Please provide NET filename') - exit(1) - - noisy_warnings() - - print('Generating constraints...\n') - - sub('symbiflow_generate_constraints', - args.eblif, args.net, args.part, vprargs.arch_def, args.pcf) - - vpr(vprargs) - - save_vpr_log('place.log') \ No newline at end of file diff --git a/f4pga/wrappers/xc7/route.py b/f4pga/wrappers/xc7/route.py deleted file mode 100644 index 305592c..0000000 --- a/f4pga/wrappers/xc7/route.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/python3 - -import argparse -import subprocess -import os -import shutil -from f4pga.wrappers.xc7.common import ( - my_path, - setup_vpr_arg_parser, - VprArgs, - noisy_warnings, - vpr -) - -def main(): - mypath = my_path() - parser = setup_vpr_arg_parser() - args = parser.parse_args() - - vprargs = VprArgs(mypath, args) - vprargs.export() - - noisy_warnings(args.device) - - vprargs.optional += '--route' - - print('Routing...') - vpr(vprargs) - - save_vpr_log('route.log') diff --git a/f4pga/wrappers/xc7/synth.py b/f4pga/wrappers/xc7/synth.py index 9d60f3f..f209c24 100755 --- a/f4pga/wrappers/xc7/synth.py +++ b/f4pga/wrappers/xc7/synth.py @@ -1,85 +1,126 @@ -#!/usr/bin/python3 +from pathlib import Path +from sys import argv as sys_argv +from os import environ +from argparse import ArgumentParser +from f4pga.wrappers import run -import sys -import os -import argparse -from f4pga.wrappers.xc7.common import * -def setup_arg_parser(): - parser = argparse.ArgumentParser(description="Parse flags") - parser.add_argument('-t', '--top', nargs=1, metavar='', - type=str, help='Top module name') - parser.add_argument('-v', '--verilog', nargs='+', metavar='', - type=str, help='Verilog file list') - parser.add_argument('-x', '--xdc', nargs='+', metavar='', - type=str, help='XDC file list') - parser.add_argument('-d', '--device', nargs=1, metavar='', - type=str, help='Device type (e.g. artix7)') - parser.add_argument('-p', '--part', nargs=1, metavar='', - type=str, help='Part name') - return parser +def arg_parser(): + parser = ArgumentParser(description="Parse flags") + + parser.add_argument( + '-t', + '--top', + nargs=1, + metavar='', + type=str, + help='Top module name' + ) + + parser.add_argument( + '-v', + '--verilog', + nargs='+', + metavar='', + type=str, + help='Verilog file list' + ) + + parser.add_argument( + '-x', + '--xdc', + nargs='+', + metavar='', + type=str, + help='XDC file list' + ) + + parser.add_argument( + '-d', + '--device', + nargs=1, + metavar='', + type=str, + help='Device type (e.g. artix7)' + ) + + parser.add_argument( + '-p', + '--part', + nargs=1, + metavar='', + type=str, + help='Part name' + ) + + return parser.parse_args() + def main(): - mypath = os.path.realpath(sys.argv[0]) - mypath = os.path.dirname(mypath) + share_dir_path = (Path(sys_argv[0]).resolve().parent / '../share/symbiflow').resolve() + utils_path = share_dir_path / 'scripts' - share_dir_path = os.path.realpath(os.path.join(mypath, '../share/symbiflow')) - techmap_path = os.path.join(share_dir_path, 'techmaps/xc7_vpr/techmap') - utils_path = os.path.join(share_dir_path, 'scripts') - synth_tcl_path = os.path.join(utils_path, 'xc7/synth.tcl') - conv_tcl_path = os.path.join(utils_path, 'xc7/conv.tcl') - split_inouts = os.path.join(utils_path, 'split_inouts.py') + environ['SHARE_DIR_PATH'] = str(share_dir_path) + environ['TECHMAP_PATH'] = str(share_dir_path / 'techmaps/xc7_vpr/techmap') + environ['UTILS_PATH'] = str(utils_path) - os.environ['SHARE_DIR_PATH'] = share_dir_path - os.environ['TECHMAP_PATH'] = techmap_path - os.environ['UTILS_PATH'] = utils_path + args = arg_parser() - parser = setup_arg_parser() - - args = parser.parse_args() - - if not os.environ['DATABASE_DIR']: - os.environ['DATABASE_DIR'] = sub(['prjxray-config']) - database_dir = os.environ['DATABASE_DIR'] + database_dir = environ.get('DATABASE_DIR', str(run('prjxray-config'))) + environ['DATABASE_DIR'] = database_dir # TODO: is this crossplatform??? - if not os.environ['PYTHON3']: - os.environ['PYTHON3'] = sub(['which', 'python3']) + if 'PYTHON3' not in environ: + environ['PYTHON3'] = run(['which', 'python3']) if not args.verilog: - print('Please provide at least one Verilog file\n') - exit(0) + raise(Exception('Please provide at least one Verilog file\n')) + if not args.top: - print('Top module must be specified\n') - exit(0) + raise(Exception('Top module must be specified\n')) + if not args.device: - print('Device parameter required\n') - exit(0) + raise(Exception('Device parameter required\n')) + if not args.part: - print('Part parameter required\n') - exit(0) + raise(Exception('Part parameter required\n')) - out_json = args.top + '.json' - synth_json = args.top + '_io.json' - log = args.top + '_synth.log' + out_json = f"{args.top}.json" + synth_json = f"{args.top}_io.json" + log = f"{args.top}_synth.log" - os.environ['TOP'] = args.top - os.environ['OUT_JSON'] = out_json - os.environ['OUT_SDC'] = args.top + '.sdc' - os.environ['SYNTH_JSON'] = synth_json - os.environ['OUT_SYNTH_V'] = args.top + '_synth.v' - os.environ['OUT_EBLIF'] = args.top + '.eblif' - os.environ['PART_JSON'] = \ - os.path.join(database_dir, args.device, args.part, 'part.json') - os.environ['OUT_FASM_EXTRA'] = args.top + '_fasm_extra.fasm' + environ['TOP'] = args.top + environ['OUT_JSON'] = out_json + environ['OUT_SDC'] = f"{args.top}.sdc" + environ['SYNTH_JSON'] = synth_json + environ['OUT_SYNTH_V'] = f"{args.top}_synth.v" + environ['OUT_EBLIF'] = f"{args.top}.eblif" + environ['PART_JSON'] = str(Path(database_dir) / f"{args.device}/{args.part}/part.json") + environ['OUT_FASM_EXTRA'] = args.top + '_fasm_extra.fasm' if args.xdc: - os.environ['INPUT_XDC_FILES'] = ' '.join(args.xdc) + environ['INPUT_XDC_FILES'] = ' '.join(args.xdc) - verilog_paths_str = ' '.join(args.verilog) + run( + 'yosys', + '-p', + f'\"tcl {(utils_path / "xc7/synth.tcl")!s}\"', + '-l', + 'log', + ' '.join(args.verilog) + ) - print('------------------------------------> In symbiflow_synth!!!\n') + run( + 'python3', + str(utils_path / 'split_inouts.py'), + '-i', + out_json, + '-o', + synth_json + ) - sub('yosys', '-p', f'\"tcl {synth_tcl_path}\"', '-l', 'log', verilog_paths_str) - sub('python3', split_inouts, '-i', out_json, '-o', synth_json) - sub('yosys', '-p', f'\"read_json {synth_json}; tcl {conv_tcl_path}\"') + run( + 'yosys', + '-p', + f'\"read_json {synth_json}; tcl {(utils_path / "xc7/conv.tcl")!s}\"' + ) diff --git a/f4pga/wrappers/xc7/vpr.py b/f4pga/wrappers/xc7/vpr.py new file mode 100644 index 0000000..7a54e1b --- /dev/null +++ b/f4pga/wrappers/xc7/vpr.py @@ -0,0 +1,133 @@ +from typing import List +from pathlib import Path +from argparse import ArgumentParser +from os import environ +from shutil import move as sh_mv + +from f4pga.wrappers import run + +class VprArgs: + arch_dir: Path + arch_def: Path + lookahead: Path + rr_graph: Path + rr_graph_xml: Path + place_delay: Path + device_name: Path + eblif: str + vpr_options: str + optional: List[str] + + def __init__(self, mypath, args): + self.arch_dir = (Path(mypath) / '../share/symbiflow/arch' / args.device).resolve() + self.arch_def = self.arch_dir / 'arch.timing.xml' + filename = f'rr_graph_{args.device}' + self.lookahead = self.arch_dir / f'{filename}.lookahead.bin' + self.rr_graph = self.arch_dir / f'{filename}.rr_graph.real.bin' + self.rr_graph_xml = self.arch_dir / f'{filename}.rr_graph.real.xml' + self.place_delay = self.arch_dir / f'{filename}.place_delay.bin' + self.device_name = args.device.replace('_', '-') + self.eblif = args.eblif + self.vpr_options = args.vpr_options + self.optional = ['--sdc_file', args.sdc] if args.sdc else [] + + def export(self): + environ['ARCH_DIR'] = str(self.arch_dir) + environ['ARCH_DEF'] = str(self.arch_def) + environ['LOOKAHEAD'] = str(self.lookahead) + environ['RR_GRAPH'] = str(self.rr_graph) + environ['RR_GRAPH_XML'] = str(self.rr_graph_xml) + environ['PLACE_DELAY'] = str(self.place_delay) + environ['DEVICE_NAME'] = str(self.device_name) + + +def setup_vpr_arg_parser(): + parser = ArgumentParser(description="Parse flags") + + parser.add_argument( + '-d', + '--device', + nargs=1, + metavar='', + type=str, + help='Device type (e.g. artix7)', + default='artix7' + ) + + parser.add_argument( + '-e', + '--eblif', + nargs=1, + metavar='', + type=str, + help='EBLIF filename' + ) + + parser.add_argument( + '-p', + '--pcf', + nargs=1, + metavar='', + type=str, + help='PCF filename' + ) + + parser.add_argument( + '-P', + '--part', + nargs=1, + metavar='', + type=str, + help='Part name' + ) + + parser.add_argument( + '-s', + '--sdc', + nargs=1, + metavar='', + type=str, + help='SDC file' + ) + + parser.add_argument( + '-a', + '--vpr_options', + metavar='', + type=str, + help='Additional VPR options' + ) + + parser.add_argument( + 'additional_vpr_args', + nargs='*', + metavar='', + type=str, + help='Additional arguments for vpr command' + ) + + return parser + + +def vpr(vprargs: VprArgs): + """ + Execute `vpr` + """ + return run( + 'vpr', + vprargs.arch_def, + vprargs.eblif, + '--device', vprargs.device_name, + vprargs.vpr_options, + '--read_rr_graph', vprargs.rr_graph, + '--read_router_lookahead', vprargs.lookahead, + 'read_placement_delay_lookup', vprargs.place_delay, + *vprargs.optional + ) + + +def save_vpr_log(filename): + """ + Save VPR log. + """ + sh_mv('vpr_stdout.log', filename) diff --git a/f4pga/wrappers/xc7/write_fasm.py b/f4pga/wrappers/xc7/write_fasm.py deleted file mode 100644 index b841370..0000000 --- a/f4pga/wrappers/xc7/write_fasm.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/python3 - -import shutil -import re -from f4pga.wrappers.xc7.common import ( - my_path, - setup_vpr_arg_parser, - VprArgs, - sub -) - -def main(): - mypath = my_path() - parser = setup_vpr_arg_parser() - args = parser.parse_args() - vprargs = VprArgs(mypath, args) - - - top = vprargs.eblif - top_ext_match = re.search('.*\\.[^.]*', vprargs.eblif) - if top_ext_match: - top = top[:top_ext_match.pos] - - fasm_extra = top + '_fasm_extra.fasm' - - noisy_warnings() - - sub('genfasm', - vprargs.arch_def, - vprargs.eblif, - '--device', vprargs.device_name, - vprargs.vpr_options, - '--read_rr_graph', vprargs.rr_graph) - - print(f'FASM extra: {fasm_extra}\n') - - # Concatenate top.fasm with extra.fasm if necessary - if os.path.isfile(fasm_extra): - print('writing final fasm') - with open(top + '.fasm', 'r+<') as top_file, open(fasm_extra) as extra_file: - cat = top_file.read() - cat += '\n' - cat += extra_file.read() - top_file.seek(0) - top_file.write(cat) - top_file.truncate() - - save_vpr_log('fasm.log')