mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-24 13:57:19 -04:00
Move sv2v script into standalone core file
FuseSoC script to run sv2v can be reused with this change.
This commit is contained in:
parent
8edcb088da
commit
c8d4f2d950
5 changed files with 50 additions and 32 deletions
190
util/sv2v_in_place.py
Executable file
190
util/sv2v_in_place.py
Executable file
|
@ -0,0 +1,190 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import List, Pattern, Tuple
|
||||
|
||||
|
||||
def read_file_list(path: str) -> List[str]:
|
||||
'''Read in a list of paths from a file, one per line.'''
|
||||
ret = []
|
||||
with open(path) as handle:
|
||||
for line in handle:
|
||||
ret.append(line.strip())
|
||||
return ret
|
||||
|
||||
|
||||
def transform_one(sv2v: str,
|
||||
defines: List[str],
|
||||
incdirs: List[str],
|
||||
pkg_paths: List[str],
|
||||
sv_path: str,
|
||||
dst_path: str) -> None:
|
||||
'''Run sv2v to edit a file in place'''
|
||||
defines_args = ['--define=' + d for d in defines]
|
||||
incdirs_args = ['--incdir=' + d for d in incdirs]
|
||||
paths = pkg_paths + ([] if sv_path in pkg_paths else [sv_path])
|
||||
|
||||
cmd = ([sv2v,
|
||||
# Pass --exclude=assert to tell sv2v not to strip out assertions.
|
||||
# Since the whole point of this flow is to prove assertions, we
|
||||
# need to leave them unscathed!
|
||||
'--exclude=assert'] +
|
||||
defines_args +
|
||||
incdirs_args +
|
||||
paths)
|
||||
logging.info('Running sv2v on {}'.format(sv_path))
|
||||
logging.debug('Command: {}'.format(cmd))
|
||||
with open(dst_path, 'w') as dst_file:
|
||||
proc = subprocess.run(cmd, stdout=dst_file)
|
||||
if proc.returncode != 0:
|
||||
cmd_str = ' '.join([shlex.quote(a) for a in cmd])
|
||||
raise RuntimeError('Failed to run sv2v on {}. '
|
||||
'Exit code: {}. Full command: {}'
|
||||
.format(sv_path, proc.returncode, cmd_str))
|
||||
|
||||
|
||||
def parse_define_if(arg: str) -> Tuple[Pattern[str], str]:
|
||||
'''Handle a --define-if argument'''
|
||||
parts = arg.rsplit(':', 1)
|
||||
if len(parts) != 2:
|
||||
msg = ('The --define-if argument {!r} contains no colon. The correct '
|
||||
'syntax is "--define-if regex:define".'
|
||||
.format(arg))
|
||||
raise argparse.ArgumentTypeError(msg)
|
||||
|
||||
re_str, define = parts
|
||||
try:
|
||||
return (re.compile(re_str), define)
|
||||
except re.error as err:
|
||||
raise argparse.ArgumentTypeError('The regex for the --define-if '
|
||||
'argument ({!r}) is malformed: {}.'
|
||||
.format(re_str, err))
|
||||
|
||||
|
||||
def transform(sv2v: str,
|
||||
defines: List[str],
|
||||
defines_if: List[Tuple[Pattern[str], str]],
|
||||
incdirs: List[str],
|
||||
pkg_paths: List[str],
|
||||
sv_paths: List[str]) -> None:
|
||||
'''Run sv2v to transform a list of files in-place'''
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# First write each file to a file in a temporary directory, then copy
|
||||
# everything back. We have to do it like this because otherwise we
|
||||
# might trash a file that needs to be included by a later one.
|
||||
dst_paths = []
|
||||
for idx, src_path in enumerate(sv_paths):
|
||||
dst_path = os.path.join(tmpdir, str(idx))
|
||||
|
||||
extra_file_defines = []
|
||||
for regex, define in defines_if:
|
||||
if regex.search(src_path):
|
||||
extra_file_defines.append(define)
|
||||
|
||||
transform_one(sv2v, defines + extra_file_defines,
|
||||
incdirs, pkg_paths, src_path, dst_path)
|
||||
dst_paths.append(dst_path)
|
||||
|
||||
# Now copy everything back, overwriting the original code
|
||||
for dst_path, src_path in zip(dst_paths, sv_paths):
|
||||
shutil.copy(dst_path, src_path)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('file_list',
|
||||
help=('File containing a list of '
|
||||
'paths on which to work.'))
|
||||
parser.add_argument('--verbose', '-v', action='store_true',
|
||||
help="Log messages about what we're doing.")
|
||||
parser.add_argument('--define', '-D', action='append', dest='defines',
|
||||
default=[],
|
||||
help='Add a preprocessor define.')
|
||||
parser.add_argument('--define-if', action='append',
|
||||
dest='defines_if', type=parse_define_if, default=[],
|
||||
help=('Add a preprocessor define which applies to '
|
||||
'specific files. For example '
|
||||
'--define-if=foo:bar would define `bar on any '
|
||||
'files whose paths contained a match for the '
|
||||
'regex "foo".'))
|
||||
parser.add_argument('--incdir', '-I', action='append', dest='incdirs',
|
||||
default=[],
|
||||
help='Add an include dir for the preprocessor.')
|
||||
parser.add_argument('--incdir-list',
|
||||
help=('Specify a file containing a list of include '
|
||||
'directories (which are appended to any defined '
|
||||
'through the --incdir argument).'))
|
||||
parser.add_argument('--sv2v',
|
||||
default='sv2v',
|
||||
help=("Specify the name or path of the sv2v binary. "
|
||||
"Defaults to 'sv2v'."))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
try:
|
||||
logging.info('Reading file list from {!r}.'.format(args.file_list))
|
||||
paths = read_file_list(args.file_list)
|
||||
except IOError:
|
||||
logging.error('Failed to read file list from {!r}'
|
||||
.format(args.file_list))
|
||||
return 1
|
||||
|
||||
if args.incdir_list is not None:
|
||||
try:
|
||||
logging.info('Reading incdir list from {!r}.'
|
||||
.format(args.incdir_list))
|
||||
args.incdirs += read_file_list(args.incdir_list)
|
||||
except IOError:
|
||||
logging.error('Failed to read incdir list from {!r}'
|
||||
.format(args.file_list))
|
||||
return 1
|
||||
|
||||
# Find all .sv or .svh files, splitting out paths ending in "pkg.sv"
|
||||
# specially. We treat these as packages, which are included in each sv2v
|
||||
# conversion.
|
||||
sv_paths = []
|
||||
svh_paths = []
|
||||
pkg_paths = []
|
||||
for path in paths:
|
||||
if os.path.splitext(path)[1] == '.sv':
|
||||
sv_paths.append(path)
|
||||
if os.path.splitext(path)[1] == '.svh':
|
||||
svh_paths.append(path)
|
||||
if path.endswith('pkg.sv'):
|
||||
pkg_paths.append(path)
|
||||
|
||||
logging.info('Running sv2v in-place on {} files ({} packages).'
|
||||
.format(len(sv_paths), len(pkg_paths)))
|
||||
|
||||
try:
|
||||
transform(args.sv2v, args.defines, args.defines_if, args.incdirs,
|
||||
pkg_paths, sv_paths)
|
||||
except RuntimeError as err:
|
||||
logging.error(err)
|
||||
return 1
|
||||
|
||||
# Empty out any remaining .svh files: they should have been included by
|
||||
# this point (sv2v includes a preprocessor).
|
||||
logging.info('Splatting contents of {} .svh files.'.format(len(svh_paths)))
|
||||
for path in svh_paths:
|
||||
with open(path, 'w'):
|
||||
pass
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
Loading…
Add table
Add a link
Reference in a new issue