diff --git a/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py b/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py index de28a519e..96bc04048 100644 --- a/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py +++ b/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py @@ -1,132 +1,242 @@ #!/usr/bin/env python3 +"""Tooling to handle .xci files for Vivado IP cores.""" +# +# Copyright 2020 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: GPL-3.0-or-later +# import argparse import os -import sys import re +import sys + # Parse command line options -def get_options(): - parser = argparse.ArgumentParser(description='Utility script to query and modify a Xilinx IP XCI file') - parser.add_argument('action', type=str, default=None, help='Action to perform') - parser.add_argument('xci_filepath', type=str, default=None, help='Name for the IP core') - parser.add_argument('--target', type=str, default=None, help='Input value for target. Must be of the form ////') - parser.add_argument('--name', type=str, default=None, help='Input value for new IP name') - parser.add_argument("--output_dir", type=str, default='.', help="Build directory for IP") +def parse_args(): + """Parse command line arguments.""" + parser = argparse.ArgumentParser( + description="Utility script to query and modify a Xilinx IP XCI file" + ) + parser.add_argument( + "action", + type=str, + default=None, + help="Action to perform", + choices=["read_target", "read_arch", "read_partid", "read_part", "retarget", "rename"], + ) + parser.add_argument("xci_filepath", type=str, default=None, help="Name for the IP core") + parser.add_argument( + "--target", + type=str, + default=None, + help="Input value for target. Must be of the form ////", + ) + parser.add_argument("--name", type=str, default=None, help="Input value for new IP name") + parser.add_argument("--output_dir", type=str, default=".", help="Build directory for IP") args = parser.parse_args() if not args.action: - print('ERROR: Please specify an action to perform\n') + print("ERROR: Please specify an action to perform\n") parser.print_help() sys.exit(1) if not args.xci_filepath: - print('ERROR: Please specify the location for the XCI file to operate on\n') + print("ERROR: Please specify the location for the XCI file to operate on\n") parser.print_help() sys.exit(1) if not os.path.isfile(args.xci_filepath): - print('ERROR: XCI File ' + args.xci_filepath + ' could not be accessed or is not a file.\n') + print(f"ERROR: XCI File {args.xci_filepath} could not be accessed or is not a file.\n") parser.print_help() sys.exit(1) return args + def get_top_level_match_str(tag_text): - return '(.*\<' + tag_text + '\>)(.+)(\)' + """Get the regex string to match a top-level tag.""" + return r"(.*\<" + tag_text + "\>)(.+)(\)" + + def get_match_str(item): - return '(.*\)(.+)(\)' + """Get the regex string to match a configurable element value.""" + return ( + r'(.*\)(.+)(\)' + ) + + def get_empty_match_str(item): - return '(.*\' + """Get the regex string to match an empty configurable element value.""" + return r'(.*\' + def main(): - args = get_options(); + """Main function.""" + args = parse_args() # Read XCI File with open(args.xci_filepath) as in_file: xci_lines = in_file.readlines() - if args.action.startswith('read_'): + if args.action.startswith("read_"): # Extract info from XCI File xci_info = dict() for line in xci_lines: - m = re.search(get_match_str('(ARCHITECTURE|DEVICE|PACKAGE|SPEEDGRADE|TEMPERATURE_GRADE|SILICON_REVISION)'), line) + m = re.search( + get_match_str( + "(ARCHITECTURE|DEVICE|PACKAGE|SPEEDGRADE|TEMPERATURE_GRADE|SILICON_REVISION)" + ), + line, + ) if m is not None: - xci_info[m.group(2)] = m.group(3) + xci_info[m.group(2)] = m.group(3) else: - m = re.search(get_empty_match_str('(ARCHITECTURE|DEVICE|PACKAGE|SPEEDGRADE|TEMPERATURE_GRADE|SILICON_REVISION)'),line) - if m is not None: - xci_info[m.group(2)] = '' - if args.action == 'read_target': - print(xci_info['ARCHITECTURE'] + '/' + xci_info['DEVICE'] + '/' + xci_info['PACKAGE'] + '/' + xci_info['SPEEDGRADE']) - if args.action == 'read_arch': - print(xci_info['ARCHITECTURE']) - if args.action == 'read_partid': - print(xci_info['DEVICE'] + '/' + xci_info['PACKAGE'] + '/' + xci_info['SPEEDGRADE'] + '/' + xci_info['TEMPERATURE_GRADE'] + '/' + xci_info['SILICON_REVISION']) - if args.action == 'read_part': - # The UltraScale+ RFSoC family ids are expected diferently in Vivado, a '-' must separate each property. - if xci_info['ARCHITECTURE'] == "zynquplusRFSOC": - print(xci_info['DEVICE'] + "-" + xci_info['PACKAGE'] + xci_info['SPEEDGRADE'] + "-" + xci_info['TEMPERATURE_GRADE']) + m = re.search( + get_empty_match_str( + "(ARCHITECTURE|DEVICE|PACKAGE|SPEEDGRADE|TEMPERATURE_GRADE|SILICON_REVISION)" + ), + line, + ) + if m is not None: + xci_info[m.group(2)] = "" + if args.action == "read_target": + print( + xci_info["ARCHITECTURE"] + + "/" + + xci_info["DEVICE"] + + "/" + + xci_info["PACKAGE"] + + "/" + + xci_info["SPEEDGRADE"] + ) + if args.action == "read_arch": + print(xci_info["ARCHITECTURE"]) + if args.action == "read_partid": + print( + xci_info["DEVICE"] + + "/" + + xci_info["PACKAGE"] + + "/" + + xci_info["SPEEDGRADE"] + + "/" + + xci_info["TEMPERATURE_GRADE"] + + "/" + + xci_info["SILICON_REVISION"] + ) + if args.action == "read_part": + # The UltraScale+ RFSoC family IDs are expected differently in + # Vivado, a '-' must separate each property. + if xci_info["ARCHITECTURE"] == "zynquplusRFSOC": + print( + xci_info["DEVICE"] + + "-" + + xci_info["PACKAGE"] + + xci_info["SPEEDGRADE"] + + "-" + + xci_info["TEMPERATURE_GRADE"] + ) else: - print(xci_info['DEVICE'] + xci_info['PACKAGE'] + xci_info['SPEEDGRADE']) - elif args.action == 'retarget': + print(xci_info["DEVICE"] + xci_info["PACKAGE"] + xci_info["SPEEDGRADE"]) + elif args.action == "retarget": # Write a new XCI file with modified target info if not os.path.isdir(args.output_dir): - print('ERROR: IP Build directory ' + args.output_dir + ' could not be accessed or is not a directory.') + print( + "ERROR: IP Build directory " + + args.output_dir + + " could not be accessed or is not a directory." + ) sys.exit(1) if not args.target: - print('ERROR: No target specified.') + print("ERROR: No target specified.") sys.exit(1) - target_tok = args.target.split('/') + target_tok = args.target.split("/") if len(target_tok) < 4: - print('ERROR: Invalid target format. Must be /////') + print( + "ERROR: Invalid target format. " + "Must be /// or " + "/////" + ) sys.exit(1) - replace_dict = {'ARCHITECTURE': target_tok[0], 'DEVICE': target_tok[1], 'PACKAGE': target_tok[2], 'SPEEDGRADE': target_tok[3], \ - 'C_XDEVICEFAMILY': target_tok[0], 'C_FAMILY': target_tok[0], 'C_XDEVICE': target_tok[1]} + replace_dict = { + "ARCHITECTURE": target_tok[0], + "DEVICE": target_tok[1], + "PACKAGE": target_tok[2], + "SPEEDGRADE": target_tok[3], + "C_XDEVICEFAMILY": target_tok[0], + "C_FAMILY": target_tok[0], + "C_XDEVICE": target_tok[1], + } if len(target_tok) > 4: - replace_dict['TEMPERATURE_GRADE'] = target_tok[4] + replace_dict["TEMPERATURE_GRADE"] = target_tok[4] if len(target_tok) > 5: - replace_dict['SILICON_REVISION'] = target_tok[5] - out_xci_filename = os.path.join(os.path.abspath(args.output_dir), os.path.basename(args.xci_filepath)) + replace_dict["SILICON_REVISION"] = target_tok[5] + out_xci_filename = os.path.join( + os.path.abspath(args.output_dir), os.path.basename(args.xci_filepath) + ) - with open(out_xci_filename, 'w') as out_file: + with open(out_xci_filename, "w") as out_file: for r_line in xci_lines: w_line = r_line - m = re.search(get_match_str('(' + '|'.join(list(replace_dict.keys())) + ')'), r_line) + m = re.search( + get_match_str("(" + "|".join(list(replace_dict.keys())) + ")"), r_line + ) if m is not None: - w_line = m.group(1) + replace_dict[m.group(2)] + m.group(4) +'\n' + w_line = m.group(1) + replace_dict[m.group(2)] + m.group(4) + "\n" else: - m = re.search(get_empty_match_str('(' + '|'.join(list(replace_dict.keys())) + ')'), r_line) + m = re.search( + get_empty_match_str("(" + "|".join(list(replace_dict.keys())) + ")"), r_line + ) if m is not None: - w_line = m.group(1) + '>' + replace_dict[m.group(2)] + '\n' + w_line = ( + m.group(1) + + ">" + + replace_dict[m.group(2)] + + "\n" + ) out_file.write(w_line) - elif args.action == 'rename': + elif args.action == "rename": # Write a new XCI file with a new name if not os.path.isdir(args.output_dir): - print('ERROR: IP Build directory ' + args.output_dir + ' could not be accessed or is not a directory.') + print( + "ERROR: IP Build directory " + + args.output_dir + + " could not be accessed or is not a directory." + ) sys.exit(1) if not args.name: - print('ERROR: No name specified.') + print("ERROR: No name specified.") sys.exit(1) new_name = args.name - replace_dict = {'Component_Name': new_name} - replace_top_level = ('spirit:instanceName', new_name) - out_xci_filename = os.path.join(os.path.abspath(args.output_dir), new_name + '.xci') + replace_dict = {"Component_Name": new_name} + replace_top_level = ("spirit:instanceName", new_name) + out_xci_filename = os.path.join(os.path.abspath(args.output_dir), new_name + ".xci") - with open(out_xci_filename, 'w') as out_file: + with open(out_xci_filename, "w") as out_file: for r_line in xci_lines: w_line = r_line - m = re.search(get_match_str('(' + '|'.join(list(replace_dict.keys())) + ')'), r_line) + m = re.search( + get_match_str("(" + "|".join(list(replace_dict.keys())) + ")"), r_line + ) if m is not None: - w_line = m.group(1) + replace_dict[m.group(2)] + m.group(4) +'\n' + w_line = m.group(1) + replace_dict[m.group(2)] + m.group(4) + "\n" else: - m = re.search(get_empty_match_str('(' + '|'.join(list(replace_dict.keys())) + ')'), r_line) + m = re.search( + get_empty_match_str("(" + "|".join(list(replace_dict.keys())) + ")"), r_line + ) if m is not None: - w_line = m.group(1) + '>' + replace_dict[m.group(2)] + '\n' + w_line = ( + m.group(1) + + ">" + + replace_dict[m.group(2)] + + "\n" + ) else: m = re.search(get_top_level_match_str(replace_top_level[0]), r_line) if m is not None: - w_line = m.group(1) + replace_top_level[1] + m.group(3) + '\n' + w_line = m.group(1) + replace_top_level[1] + m.group(3) + "\n" out_file.write(w_line) -if __name__ == '__main__': +if __name__ == "__main__": main()