Source code for adi_lg_plugins.resources.xilinxtool

"""Xilinx Vivado/Vitis tool installation configuration resource."""

import glob
import os
import subprocess
import tempfile

import attr
from labgrid.factory import target_factory
from labgrid.resource import Resource


[docs] @target_factory.reg_resource @attr.s(eq=False) class XilinxVivadoTool(Resource): """Xilinx Vivado/Vitis tool installation configuration. Specifies paths to Xilinx tools (xsdb, xsct) for JTAG operations. Automatically locates xsdb binary and provides methods to execute TCL scripts via xsdb. Attributes: vivado_path (str, optional): Root path to Xilinx Vivado installation (default: "/tools/Xilinx/Vivado"). Example: "/opt/Xilinx/Vivado/2023.2" or "/tools/Xilinx/2025.1/Vivado" version (str, optional): Vivado version string (e.g., "2023.2"). Used to locate vivado_path if not explicitly set. """ name = attr.ib(default="xilinxvivadotool") # Vivado installation path vivado_path = attr.ib( default="/tools/Xilinx/2025.1/Vivado", validator=attr.validators.instance_of(str), ) # Version (e.g., "2023.2") version = attr.ib( default=None, validator=attr.validators.optional(attr.validators.instance_of(str)), ) # Derived paths (computed in __attrs_post_init__) xsdb_path = attr.ib(init=False, default=None) settings_path = attr.ib(init=False, default=None)
[docs] def __attrs_post_init__(self): """Initialize xsdb_path based on vivado_path and version.""" super().__attrs_post_init__() if self.vivado_path: if not os.path.exists(self.vivado_path): raise ValueError(f"Vivado path does not exist: {self.vivado_path}") else: if not self.version: raise ValueError("Either vivado_path or version must be specified") path1 = f"/tools/Xilinx/{self.version}/Vivado" path2 = f"/opt/Xilinx/Vivado/{self.version}" path3 = f"/opt/Xilinx/{self.version}/Vivado" for p in (path1, path2, path3): if os.path.exists(p): self.vivado_path = p break if not self.vivado_path: raise ValueError(f"Vivado path not found for version {self.version}") # Use glob to find settings.sh files = glob.glob(os.path.join(self.vivado_path, "**", "settings64.sh"), recursive=True) if not files: raise ValueError(f"settings64.sh not found in {self.vivado_path}") self.settings_path = files[0] self.logger.info(f"Found Vivado settings script at: {self.settings_path}") # Source script and find xsdb cmd = f"bash -c 'source {self.settings_path} && which xsdb'" result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if result.returncode != 0: raise ValueError(f"Failed to locate xsdb: {result.stderr.strip()}") xsdb = result.stdout.strip() if not os.path.exists(xsdb): raise ValueError(f"xsdb not found at {xsdb}") self.logger.info(f"xsdb located at: {xsdb}") self.xsdb_path = xsdb
[docs] def run_xsdb_script(self, tcl_script: str) -> tuple: """Execute TCL script via xsdb. Args: tcl_script (str): TCL commands to execute. Will be written to a temporary file and executed by xsdb. Returns: tuple: (stdout, stderr, returncode) where stdout and stderr are strings. Raises: RuntimeError: If xsdb execution fails or times out. Example: >>> tool = XilinxVivadoTool() >>> stdout, stderr, rc = tool.run_xsdb_script( ... "connect\\nafter 1000\\nfpga -f design.bit\\ndisconnect" ... ) >>> if rc == 0: ... print("Success:", stdout) ... else: ... print("Error:", stderr) """ # Write TCL to temporary file with tempfile.NamedTemporaryFile(mode="w", suffix=".tcl", delete=False) as f: f.write(tcl_script) tcl_file = f.name try: # Execute xsdb with script result = subprocess.run( [self.xsdb_path, tcl_file], capture_output=True, text=True, timeout=300, ) return result.stdout, result.stderr, result.returncode finally: # Clean up temporary file if os.path.exists(tcl_file): os.unlink(tcl_file)