From dc58d412b93d5fc7203425c65817314460d4f567 Mon Sep 17 00:00:00 2001 From: Unai Martinez-Corral Date: Fri, 4 Mar 2022 00:15:32 +0100 Subject: [PATCH] mv python toolchain wrappers into f4pga Signed-off-by: Unai Martinez-Corral --- .github/workflows/Pipeline.yml | 53 +++++++++++- f4pga/setup.py | 5 ++ .../wrappers/xc7}/__init__.py | 0 .../wrappers/xc7/common.py | 14 +-- f4pga/wrappers/xc7/place.py | 34 ++++++++ f4pga/wrappers/xc7/route.py | 30 +++++++ f4pga/wrappers/xc7/synth.py | 85 +++++++++++++++++++ f4pga/wrappers/xc7/write_fasm.py | 48 +++++++++++ xc/xc7/toolchain_wrappers/symbiflow_place.py | 28 ------ xc/xc7/toolchain_wrappers/symbiflow_route.py | 23 ----- xc/xc7/toolchain_wrappers/symbiflow_synth.py | 84 ------------------ .../symbiflow_write_fasm.py | 42 --------- 12 files changed, 260 insertions(+), 186 deletions(-) rename {xc/xc7/toolchain_wrappers => f4pga/wrappers/xc7}/__init__.py (100%) rename xc/xc7/toolchain_wrappers/symbiflow_common.py => f4pga/wrappers/xc7/common.py (91%) create mode 100644 f4pga/wrappers/xc7/place.py create mode 100644 f4pga/wrappers/xc7/route.py create mode 100755 f4pga/wrappers/xc7/synth.py create mode 100644 f4pga/wrappers/xc7/write_fasm.py delete mode 100644 xc/xc7/toolchain_wrappers/symbiflow_place.py delete mode 100644 xc/xc7/toolchain_wrappers/symbiflow_route.py delete mode 100755 xc/xc7/toolchain_wrappers/symbiflow_synth.py delete mode 100644 xc/xc7/toolchain_wrappers/symbiflow_write_fasm.py diff --git a/.github/workflows/Pipeline.yml b/.github/workflows/Pipeline.yml index c8a08cf..6cc03a2 100644 --- a/.github/workflows/Pipeline.yml +++ b/.github/workflows/Pipeline.yml @@ -132,7 +132,7 @@ jobs: pyF4PGA: runs-on: ubuntu-latest - name: '🐍 Example' + name: '🐍 Example | xc7' env: F4PGA_INSTALL_DIR: /opt/f4pga F4PGA_FAM: xc7 @@ -155,7 +155,7 @@ jobs: pip install --use-feature=in-tree-build . cd .. - - name: 🚧 Test py4FPGA build + - name: 🚧 Test f4pga build run: | . ./.github/scripts/activate.sh @@ -168,7 +168,54 @@ jobs: name: arty_35-Bitstream-pyF4PGA path: f4pga-examples/build/arty_35/top.bit - - name: Test py4FPGA (PYTHONPATH) + + PYTHONPATH: + runs-on: ubuntu-latest + name: '🐍 PYTHONPATH' + env: + F4PGA_INSTALL_DIR: /opt/f4pga + F4PGA_FAM: xc7 + + steps: + + - name: 🧰 Checkout + uses: actions/checkout@v3 + + - name: 🚧 Test pyF4PGA (PYTHONPATH) run: | PYTHONPATH=$(pwd) python3 f4pga/__init__.py PYTHONPATH=$(pwd) python3 f4pga/__init__.py -h + + + pyWrappers: + runs-on: ubuntu-latest + name: '🐍 Python wrappers' + env: + F4PGA_INSTALL_DIR: /opt/f4pga + F4PGA_FAM: xc7 + + steps: + + - name: 🧰 Checkout + uses: actions/checkout@v3 + + - name: 🔧 Prepare environment + run: ./.github/scripts/prepare_environment.sh + + - name: 🐍 Install f4pga (pip) + run: | + . ./.github/scripts/activate.sh + + cd f4pga + pip install --use-feature=in-tree-build . + cd .. + + - name: 🚦 Test Python wrappers + run: | + . ./.github/scripts/activate.sh + + for tool in place route synth write-fasm; do + echo "::group::Test f4pga-$tool" + "f4pga-$tool" || echo "Failing?" + echo "::endgroup::" + done; diff --git a/f4pga/setup.py b/f4pga/setup.py index fac3b53..d51e01f 100644 --- a/f4pga/setup.py +++ b/f4pga/setup.py @@ -80,6 +80,7 @@ setuptools_setup( "f4pga", "f4pga.common_modules", "f4pga.wrappers.sh", + "f4pga.wrappers.xc7" ], package_dir={"f4pga": "."}, package_data={ @@ -92,6 +93,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-synth = f4pga.wrappers.xc7.synth:main", + "f4pga-write-fasm = f4pga.wrappers.xc7.write_fasm:main", ] + wrapper_entrypoints }, ) diff --git a/xc/xc7/toolchain_wrappers/__init__.py b/f4pga/wrappers/xc7/__init__.py similarity index 100% rename from xc/xc7/toolchain_wrappers/__init__.py rename to f4pga/wrappers/xc7/__init__.py diff --git a/xc/xc7/toolchain_wrappers/symbiflow_common.py b/f4pga/wrappers/xc7/common.py similarity index 91% rename from xc/xc7/toolchain_wrappers/symbiflow_common.py rename to f4pga/wrappers/xc7/common.py index 41f8b45..a2306b1 100644 --- a/xc/xc7/toolchain_wrappers/symbiflow_common.py +++ b/f4pga/wrappers/xc7/common.py @@ -1,7 +1,9 @@ +from pathlib import Path import subprocess import argparse import os import shutil +from sys import argv as sys_argv class VprArgs: arch_dir: str @@ -17,7 +19,7 @@ class VprArgs: def __init__(self, mypath, args): self.arch_dir = \ - os.path.join(mypath, '../share/symbiflow/arch/', args.device) + 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 = \ @@ -38,7 +40,7 @@ class VprArgs: 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 @@ -51,7 +53,7 @@ class VprArgs: 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)') + 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='', @@ -60,7 +62,7 @@ def setup_vpr_arg_parser(): type=str, help='Part name') parser.add_argument('-s', '--sdc', nargs=1, metavar='', type=str, help='SDC file') - parser.add_argument('-a', '--additional_vpr_options', metavar='', + 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') @@ -91,9 +93,9 @@ def noisy_warnings(device): # Get current PWD def my_path(): - mypath = os.path.realpath(sys.argv[0]) + 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) \ No newline at end of file + shutil.move('vpr_stdout.log', filename) diff --git a/f4pga/wrappers/xc7/place.py b/f4pga/wrappers/xc7/place.py new file mode 100644 index 0000000..c253b1a --- /dev/null +++ b/f4pga/wrappers/xc7/place.py @@ -0,0 +1,34 @@ +#!/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 new file mode 100644 index 0000000..305592c --- /dev/null +++ b/f4pga/wrappers/xc7/route.py @@ -0,0 +1,30 @@ +#!/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 new file mode 100755 index 0000000..9d60f3f --- /dev/null +++ b/f4pga/wrappers/xc7/synth.py @@ -0,0 +1,85 @@ +#!/usr/bin/python3 + +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 main(): + mypath = os.path.realpath(sys.argv[0]) + mypath = os.path.dirname(mypath) + + 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') + + os.environ['SHARE_DIR_PATH'] = share_dir_path + os.environ['TECHMAP_PATH'] = techmap_path + os.environ['UTILS_PATH'] = utils_path + + 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'] + + # TODO: is this crossplatform??? + if not os.environ['PYTHON3']: + os.environ['PYTHON3'] = sub(['which', 'python3']) + + if not args.verilog: + print('Please provide at least one Verilog file\n') + exit(0) + if not args.top: + print('Top module must be specified\n') + exit(0) + if not args.device: + print('Device parameter required\n') + exit(0) + if not args.part: + print('Part parameter required\n') + exit(0) + + out_json = args.top + '.json' + synth_json = args.top + '_io.json' + log = 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' + + if args.xdc: + os.environ['INPUT_XDC_FILES'] = ' '.join(args.xdc) + + verilog_paths_str = ' '.join(args.verilog) + + print('------------------------------------> In symbiflow_synth!!!\n') + + 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}\"') diff --git a/f4pga/wrappers/xc7/write_fasm.py b/f4pga/wrappers/xc7/write_fasm.py new file mode 100644 index 0000000..b841370 --- /dev/null +++ b/f4pga/wrappers/xc7/write_fasm.py @@ -0,0 +1,48 @@ +#!/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') diff --git a/xc/xc7/toolchain_wrappers/symbiflow_place.py b/xc/xc7/toolchain_wrappers/symbiflow_place.py deleted file mode 100644 index e3a016d..0000000 --- a/xc/xc7/toolchain_wrappers/symbiflow_place.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/python3 - -import shutil -from symbiflow_common import * - -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/xc/xc7/toolchain_wrappers/symbiflow_route.py b/xc/xc7/toolchain_wrappers/symbiflow_route.py deleted file mode 100644 index 099260c..0000000 --- a/xc/xc7/toolchain_wrappers/symbiflow_route.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/python3 - -import argparse -import subprocess -import os -import shutil -from symbiflow_common import * - -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') \ No newline at end of file diff --git a/xc/xc7/toolchain_wrappers/symbiflow_synth.py b/xc/xc7/toolchain_wrappers/symbiflow_synth.py deleted file mode 100755 index ae97457..0000000 --- a/xc/xc7/toolchain_wrappers/symbiflow_synth.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/python3 - -import sys -import os -import argparse -from symbiflow_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 - -mypath = os.path.realpath(sys.argv[0]) -mypath = os.path.dirname(mypath) - -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') - -os.environ['SHARE_DIR_PATH'] = share_dir_path -os.environ['TECHMAP_PATH'] = techmap_path -os.environ['UTILS_PATH'] = utils_path - -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'] - -# TODO: is this crossplatform??? -if not os.environ['PYTHON3']: - os.environ['PYTHON3'] = sub(['which', 'python3']) - -if not args.verilog: - print('Please provide at least one Verilog file\n') - exit(0) -if not args.top: - print('Top module must be specified\n') - exit(0) -if not args.device: - print('Device parameter required\n') - exit(0) -if not args.part: - print('Part parameter required\n') - exit(0) - -out_json = args.top + '.json' -synth_json = args.top + '_io.json' -log = 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' - -if args.xdc: - os.environ['INPUT_XDC_FILES'] = ' '.join(args.xdc) - -verilog_paths_str = ' '.join(args.verilog) - -print('------------------------------------> In symbiflow_synth!!!\n') - -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}\"') diff --git a/xc/xc7/toolchain_wrappers/symbiflow_write_fasm.py b/xc/xc7/toolchain_wrappers/symbiflow_write_fasm.py deleted file mode 100644 index 373088f..0000000 --- a/xc/xc7/toolchain_wrappers/symbiflow_write_fasm.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/python3 - -import shutil -import re -from symbiflow_common import * - -mypath = my_path() -parser = setup_vpr_arg_parser() -args = parser.parse_args() -vprargs = VprArgs(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')