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 <umartinezcorral@antmicro.com>
This commit is contained in:
Unai Martinez-Corral 2022-03-04 01:02:11 +01:00
parent dc58d412b9
commit d0641bdf53
9 changed files with 383 additions and 280 deletions

View File

@ -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
},
)

View File

@ -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)

View File

@ -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='<net file>',
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')

View File

@ -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='<device>',
type=str, help='Device type (e.g. artix7)', default='artix7')
parser.add_argument('-e', '--eblif', nargs=1, metavar='<eblif file>',
type=str, help='EBLIF filename')
parser.add_argument('-p', '--pcf', nargs=1, metavar='<pcf file>',
type=str, help='PCF filename')
parser.add_argument('-P', '--part', nargs=1, metavar='<name>',
type=str, help='Part name')
parser.add_argument('-s', '--sdc', nargs=1, metavar='<sdc file>',
type=str, help='SDC file')
parser.add_argument('-a', '--vpr_options', metavar='<opts>',
type=str, help='Additional VPR options')
parser.add_argument('additional_vpr_args', nargs='*', metavar='<args>',
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)

View File

@ -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='<net file>',
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')

View File

@ -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')

View File

@ -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='<top module name>',
type=str, help='Top module name')
parser.add_argument('-v', '--verilog', nargs='+', metavar='<verilog files>',
type=str, help='Verilog file list')
parser.add_argument('-x', '--xdc', nargs='+', metavar='<xdc files>',
type=str, help='XDC file list')
parser.add_argument('-d', '--device', nargs=1, metavar='<device>',
type=str, help='Device type (e.g. artix7)')
parser.add_argument('-p', '--part', nargs=1, metavar='<name>',
type=str, help='Part name')
return parser
def arg_parser():
parser = ArgumentParser(description="Parse flags")
parser.add_argument(
'-t',
'--top',
nargs=1,
metavar='<top module name>',
type=str,
help='Top module name'
)
parser.add_argument(
'-v',
'--verilog',
nargs='+',
metavar='<verilog files>',
type=str,
help='Verilog file list'
)
parser.add_argument(
'-x',
'--xdc',
nargs='+',
metavar='<xdc files>',
type=str,
help='XDC file list'
)
parser.add_argument(
'-d',
'--device',
nargs=1,
metavar='<device>',
type=str,
help='Device type (e.g. artix7)'
)
parser.add_argument(
'-p',
'--part',
nargs=1,
metavar='<name>',
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}\"'
)

133
f4pga/wrappers/xc7/vpr.py Normal file
View File

@ -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='<device>',
type=str,
help='Device type (e.g. artix7)',
default='artix7'
)
parser.add_argument(
'-e',
'--eblif',
nargs=1,
metavar='<eblif file>',
type=str,
help='EBLIF filename'
)
parser.add_argument(
'-p',
'--pcf',
nargs=1,
metavar='<pcf file>',
type=str,
help='PCF filename'
)
parser.add_argument(
'-P',
'--part',
nargs=1,
metavar='<name>',
type=str,
help='Part name'
)
parser.add_argument(
'-s',
'--sdc',
nargs=1,
metavar='<sdc file>',
type=str,
help='SDC file'
)
parser.add_argument(
'-a',
'--vpr_options',
metavar='<opts>',
type=str,
help='Additional VPR options'
)
parser.add_argument(
'additional_vpr_args',
nargs='*',
metavar='<args>',
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)

View File

@ -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')