aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2024-02-09 18:48:09 +0100
committerAki Tomita <121511582+atomita-ni@users.noreply.github.com>2024-02-25 16:54:12 -0600
commit09f3453f39363870f1c03ef1c917a0dedb7598df (patch)
tree1709683b2e144a1f4821e1d3738f179f91dd79de
parentfpga: ci: Drop fetchDepth to 1 (diff)
downloaduhd-09f3453f39363870f1c03ef1c917a0dedb7598df.tar.xz
uhd-09f3453f39363870f1c03ef1c917a0dedb7598df.zip
fpga: Update tooling to use image builder instead of make
This modifies repeat_fpga_build.py such that it can take a YAML file as input, not just a make target. It will then use the image builder to prepare the build and generate a valid make command, which in turn is used to build FPGA images repeatedly.
-rwxr-xr-xfpga/usrp3/tools/utils/repeat_fpga_build.py71
-rw-r--r--host/python/uhd/imgbuilder/image_builder.py25
2 files changed, 75 insertions, 21 deletions
diff --git a/fpga/usrp3/tools/utils/repeat_fpga_build.py b/fpga/usrp3/tools/utils/repeat_fpga_build.py
index dec734a26..7f013ec61 100755
--- a/fpga/usrp3/tools/utils/repeat_fpga_build.py
+++ b/fpga/usrp3/tools/utils/repeat_fpga_build.py
@@ -33,10 +33,16 @@ def parse_args():
"--target",
"-t",
type=str,
- required=True,
help="FPGA make target to build (e.g., X310_XG).",
)
parser.add_argument(
+ "--image-core",
+ "-y",
+ type=str,
+ help="For using the image builder instead of make, use this to specify "
+ "the image core YAML.",
+ )
+ parser.add_argument(
"--num",
"-n",
type=int,
@@ -65,12 +71,52 @@ def parse_args():
return parser.parse_args()
-def run_fpga_build(build_seed, target):
+def prepare_build(target, image_core):
+ """
+ Run tasks to prepare the build. In particular, execute the RFNoC image builder
+ if desired.
+ """
+ if image_core:
+ logging.info("Calling rfnoc_image_builder to prepare FPGA build.")
+ cmd = [
+ "rfnoc_image_builder",
+ "--yaml-config",
+ image_core,
+ "--generate-only",
+ "--no-hash",
+ "--no-date",
+ ]
+ if target:
+ cmd += ["--target", target]
+ result = subprocess.run(
+ cmd,
+ check=False,
+ encoding="utf-8",
+ capture_output=True,
+ )
+ logging.info("rfnoc_image_builder output:")
+ logging.info("stdout:\n%s", result.stdout)
+ logging.info("stderr:\n%s", result.stderr)
+ if result.returncode:
+ logging.error("Image builder failed! Consult output for details.")
+ result.check_returncode()
+ # Parse the image builder output to get the make command we need to build the FPGA
+ make_command = re.search(r"(?<=: )make.*$", result.stderr, flags=re.M).group(0)
+ logging.info("Using make command: %s", make_command)
+ else:
+ assert target
+ make_command = f"make {target}"
+ return {
+ "make_command": make_command,
+ }
+
+
+def run_fpga_build(build_seed, cfg):
"""Performs one iteration of an FPGA build.
Args:
build_seed: 32-bit signed integer to seed the FPGA build.
- target: Make target name (e.g., X310_XG).
+ cfg: Build configuration info
Returns:
0: The build succeeded.
@@ -78,7 +124,8 @@ def run_fpga_build(build_seed, target):
2: There was some other error that should cause us to stop trying.
"""
output = ""
- cmd = f'/bin/bash -c "make {target} BUILD_SEED={build_seed}"'
+ make_cmd = cfg["make_command"]
+ cmd = f'/bin/bash -c "{make_cmd} BUILD_SEED={build_seed}"'
with subprocess.Popen(
cmd,
shell=True,
@@ -134,17 +181,21 @@ def main():
logging.basicConfig(format="[REPEAT BUILD][%(levelname)s] %(message)s")
logging.root.setLevel(logging.INFO)
args = parse_args()
+ if not args.target and not args.image_core:
+ logging.error("Either --target or --image-core must be provided!")
+ return 1
build_seed = args.seed
status = 128
+ cfg = prepare_build(args.target, args.image_core)
try:
for build_num in range(1, args.num + 1):
- logging.info(f"Starting FPGA build {build_num} with seed {build_seed}")
- status = run_fpga_build(build_seed, args.target)
- logging.info(f"Finished FPGA build {build_num}")
+ logging.info("Starting FPGA build %d with seed %s", build_num, build_seed)
+ status = run_fpga_build(build_seed, cfg)
+ logging.info("Finished FPGA build %d", build_num)
if status == 0:
- logging.info(f"FPBA build succeeded on attempt number {build_num}")
+ logging.info("FPGA build succeeded on attempt number %s", build_num)
break
- elif build_num == args.num:
+ if build_num == args.num:
logging.error("Reached maximum number of FPGA build attempts")
elif status == 1:
logging.info("FPGA build will be restarted due to unsuccessful attempt")
@@ -152,7 +203,7 @@ def main():
logging.error("Stopping due to unexpected FPGA build error")
break
build_seed = next_build_seed(build_seed)
- except (KeyboardInterrupt):
+ except KeyboardInterrupt:
logging.info("Received SIGINT. Aborting . . .")
# Return normal Bash value for SIGINT (128+2)
return 130
diff --git a/host/python/uhd/imgbuilder/image_builder.py b/host/python/uhd/imgbuilder/image_builder.py
index 14d15027a..3228e57c2 100644
--- a/host/python/uhd/imgbuilder/image_builder.py
+++ b/host/python/uhd/imgbuilder/image_builder.py
@@ -977,26 +977,33 @@ def build(fpga_path, device, image_core_path, edge_file, **args):
] + args.get("extra_makefile_srcs", [])
logging.debug("Temporarily changing working directory to %s", build_dir)
os.chdir(build_dir)
- make_cmd = ". ./setupenv.sh "
+ setup_cmd = ". ./setupenv.sh "
if "vivado_path" in args and args["vivado_path"]:
- make_cmd = make_cmd + "--vivado-path=" + args["vivado_path"] + " "
+ setup_cmd += "--vivado-path=" + args["vivado_path"] + " "
+ make_cmd = ""
if "clean_all" in args and args["clean_all"]:
- make_cmd = make_cmd + "&& make cleanall "
+ make_cmd += "make cleanall && "
target = args["target"] if "target" in args else ""
- make_cmd = make_cmd + "&& make " + default_target(device, target)
- make_cmd += " IMAGE_CORE={} EDGE_FILE={}".format(image_core_path,
- edge_file)
+ make_cmd += "make " + default_target(device, target)
+ make_cmd += f" IMAGE_CORE={image_core_path} EDGE_FILE={edge_file}"
if makefile_src_paths:
make_cmd += " RFNOC_OOT_MAKEFILE_SRCS=" + "\\ ".join(makefile_src_paths)
if "GUI" in args and args["GUI"]:
make_cmd = make_cmd + " GUI=1"
+
+ if args.get('generate_only'):
+ logging.info("Skip build (generate only option given)")
+ logging.info("Use the following command to build the image: %s", make_cmd)
+ return 0
+
+ make_cmd = setup_cmd + " && " + make_cmd
logging.info("Launching build with the following settings:")
logging.info(" * Build Directory: %s", build_dir)
logging.info(" * Target: %s", target)
logging.info(" * Image Core File: %s", image_core_path)
logging.info(" * Edge Table File: %s", edge_file)
# Wrap it into a bash call:
- make_cmd = '{bash} -c "{cmd}"'.format(bash=BASH_EXECUTABLE, cmd=make_cmd)
+ make_cmd = f'{BASH_EXECUTABLE} -c "{make_cmd}"'
logging.debug("Executing the following command: %s", make_cmd)
ret_val = os.system(make_cmd)
os.chdir(cwd)
@@ -1134,10 +1141,6 @@ def build_image(config, fpga_path, config_path, device, **args):
)
write_build_env()
- if "generate_only" in args and args["generate_only"]:
- logging.info("Skip build (generate only option given)")
- return 0
-
# Check if the YAML files require additional Makefile.srcs
extra_makefile_srcs = set()
for block_info in builder_conf.noc_blocks.values():