uhd/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py
Martin Braun 0dede88c65 fpga: tools: Refactor viv_ip_xci_editor.py
This Python utility was getting a bit rotten. The following updates
where made:

- Reformatted using ni-python-styleguide
- Fixed formatting of regex strings by declaring them 'raw'
- Updated docstrings and any linter issues

On Pyton 3.12 and beyond, this will no longer throw SyntaxError
warnings.
2025-02-07 14:52:28 +01:00

242 lines
8.6 KiB
Python

#!/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 re
import sys
# Parse command line options
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 <arch>/<device>/<package>/<speedgrade>/<silicon revision>",
)
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")
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")
parser.print_help()
sys.exit(1)
if not os.path.isfile(args.xci_filepath):
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):
"""Get the regex string to match a top-level tag."""
return r"(.*\<" + tag_text + "\>)(.+)(\</" + tag_text + r"\>)"
def get_match_str(item):
"""Get the regex string to match a configurable element value."""
return (
r'(.*\<spirit:configurableElementValue spirit:referenceId=".*\.'
+ item
+ r'"\>)(.+)(\</spirit:configurableElementValue\>)'
)
def get_empty_match_str(item):
"""Get the regex string to match an empty configurable element value."""
return r'(.*\<spirit:configurableElementValue spirit:referenceId=".*\.' + item + r'")/\>'
def main():
"""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_"):
# 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,
)
if m is not None:
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 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":
# 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."
)
sys.exit(1)
if not args.target:
print("ERROR: No target specified.")
sys.exit(1)
target_tok = args.target.split("/")
if len(target_tok) < 4:
print(
"ERROR: Invalid target format. "
"Must be <arch>/<device>/<package>/<speedgrade> or "
"<arch>/<device>/<package>/<speedgrade>/<tempgrade>/<silicon revision>"
)
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],
}
if len(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)
)
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
)
if m is not None:
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
)
if m is not None:
w_line = (
m.group(1)
+ ">"
+ replace_dict[m.group(2)]
+ "</spirit:configurableElementValue>\n"
)
out_file.write(w_line)
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."
)
sys.exit(1)
if not args.name:
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")
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
)
if m is not None:
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
)
if m is not None:
w_line = (
m.group(1)
+ ">"
+ replace_dict[m.group(2)]
+ "</spirit:configurableElementValue>\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"
out_file.write(w_line)
if __name__ == "__main__":
main()