✨ feat: Xilinx Kit directory selector + multi-version scan
Configuration panel now has a 'Xilinx Kit:' row at the top with: - Entry field showing current path - [Browse] button using system directory dialog (cross-platform) - Select root directory (e.g. /data/xilinx or C:/Xilinx) vitis_checker.py now scans xilinx_root subdirectories: 1. System PATH (shutil.which) — works on all platforms 2. Legacy vitis_path/bin 3. xilinx_root/Vitis/*/bin and Vivado/*/bin (all versions, newest first) 4. Common install paths (Windows: C:/Xilinx, Linux: /data/xilinx, /opt/xilinx) Multi-version: scans all version directories under Vitis/ and Vivado/, picks the newest version for each tool. Config: xilinx_path replaces vitis_path (backward compat kept)
This commit is contained in:
@@ -21,5 +21,6 @@ step_timeouts:
|
|||||||
tftp_reboot: 120
|
tftp_reboot: 120
|
||||||
tftp_upload_name: z7bin
|
tftp_upload_name: z7bin
|
||||||
uart_delay: 3
|
uart_delay: 3
|
||||||
|
xilinx_path: ""
|
||||||
vitis_path: ''
|
vitis_path: ''
|
||||||
zynq_ip: 192.168.100.11
|
zynq_ip: 192.168.100.11
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
# Default configuration for Zynq XC7Z100 Flasher
|
# Default configuration for Zynq XC7Z100 Flasher
|
||||||
# Paths are relative to this config file's directory.
|
# Paths are relative to this config file's directory.
|
||||||
|
|
||||||
# Vitis/Vivado installation path (where bootgen, impact, etc. reside)
|
# Xilinx installation root (e.g., /data/xilinx or C:/Xilinx)
|
||||||
vitis_path: ""
|
# The program scans subdirectories for Vitis/Vivado versions.
|
||||||
|
# If empty, tools are searched via system PATH.
|
||||||
|
xilinx_path: ""
|
||||||
|
|
||||||
# Zynq device network settings
|
# Zynq device network settings
|
||||||
zynq_ip: "192.168.100.11"
|
zynq_ip: "192.168.100.11"
|
||||||
|
|||||||
@@ -166,7 +166,10 @@ def program_bitstream(
|
|||||||
ps7_tcl = _find_ps7_init_tcl(config)
|
ps7_tcl = _find_ps7_init_tcl(config)
|
||||||
|
|
||||||
# Try xsct first (preferred)
|
# Try xsct first (preferred)
|
||||||
xsct = get_xsct_path(config.vitis_path)
|
xsct = get_xsct_path(
|
||||||
|
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
||||||
|
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
||||||
|
)
|
||||||
if xsct:
|
if xsct:
|
||||||
timeout = config.step_timeouts.get("bitstream", 60)
|
timeout = config.step_timeouts.get("bitstream", 60)
|
||||||
return _program_with_xsct(xsct, bit_path, callback, ps7_tcl, timeout)
|
return _program_with_xsct(xsct, bit_path, callback, ps7_tcl, timeout)
|
||||||
@@ -340,7 +343,10 @@ def run_elf(
|
|||||||
callback("start", f"Running ELF: {elf_path.name}...")
|
callback("start", f"Running ELF: {elf_path.name}...")
|
||||||
|
|
||||||
# Use xsct for CPU operations (Vivado HM cannot program PS CPU)
|
# Use xsct for CPU operations (Vivado HM cannot program PS CPU)
|
||||||
xsct = get_xsct_path(config.vitis_path)
|
xsct = get_xsct_path(
|
||||||
|
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
||||||
|
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
||||||
|
)
|
||||||
if not xsct:
|
if not xsct:
|
||||||
return BitstreamResult(
|
return BitstreamResult(
|
||||||
step="elf",
|
step="elf",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import yaml
|
|||||||
|
|
||||||
|
|
||||||
DEFAULT_CONFIG: dict[str, Any] = {
|
DEFAULT_CONFIG: dict[str, Any] = {
|
||||||
"vitis_path": "",
|
"xilinx_path": "",
|
||||||
"zynq_ip": "192.168.100.11",
|
"zynq_ip": "192.168.100.11",
|
||||||
"tftp_upload_name": "z7bin",
|
"tftp_upload_name": "z7bin",
|
||||||
"serial_port": "",
|
"serial_port": "",
|
||||||
@@ -52,7 +52,7 @@ class Config:
|
|||||||
directory and resolved to absolute paths when accessed.
|
directory and resolved to absolute paths when accessed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
vitis_path: str = ""
|
xilinx_path: str = ""
|
||||||
zynq_ip: str = "192.168.100.11"
|
zynq_ip: str = "192.168.100.11"
|
||||||
tftp_upload_name: str = "z7bin"
|
tftp_upload_name: str = "z7bin"
|
||||||
serial_port: str = ""
|
serial_port: str = ""
|
||||||
@@ -160,7 +160,7 @@ class Config:
|
|||||||
Dictionary representation of the config.
|
Dictionary representation of the config.
|
||||||
"""
|
"""
|
||||||
data = {
|
data = {
|
||||||
"vitis_path": self._relative_path(self.vitis_path),
|
"xilinx_path": self.xilinx_path,
|
||||||
"zynq_ip": self.zynq_ip,
|
"zynq_ip": self.zynq_ip,
|
||||||
"tftp_upload_name": self.tftp_upload_name,
|
"tftp_upload_name": self.tftp_upload_name,
|
||||||
"serial_port": self.serial_port,
|
"serial_port": self.serial_port,
|
||||||
|
|||||||
@@ -112,7 +112,10 @@ def wipe_flash(
|
|||||||
Returns:
|
Returns:
|
||||||
FlashResult with status.
|
FlashResult with status.
|
||||||
"""
|
"""
|
||||||
flash_tool = get_program_flash_path(config.vitis_path)
|
flash_tool = get_program_flash_path(
|
||||||
|
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
||||||
|
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
||||||
|
)
|
||||||
if not flash_tool:
|
if not flash_tool:
|
||||||
return FlashResult(
|
return FlashResult(
|
||||||
step="wipe",
|
step="wipe",
|
||||||
@@ -225,7 +228,10 @@ def program_flash_bin(
|
|||||||
message=f"BIN file not found: {bin_path}",
|
message=f"BIN file not found: {bin_path}",
|
||||||
)
|
)
|
||||||
|
|
||||||
flash_tool = get_program_flash_path(config.vitis_path)
|
flash_tool = get_program_flash_path(
|
||||||
|
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
||||||
|
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
||||||
|
)
|
||||||
if not flash_tool:
|
if not flash_tool:
|
||||||
return FlashResult(
|
return FlashResult(
|
||||||
step="program",
|
step="program",
|
||||||
|
|||||||
+35
-8
@@ -182,6 +182,8 @@ class MainWindow(ctk.CTk):
|
|||||||
# Sync IP address
|
# Sync IP address
|
||||||
if self._ip_string_var:
|
if self._ip_string_var:
|
||||||
self._config.zynq_ip = self._ip_string_var.get()
|
self._config.zynq_ip = self._ip_string_var.get()
|
||||||
|
# Sync Xilinx Kit path
|
||||||
|
self._config.xilinx_path = self._xilinx_path_var.get()
|
||||||
# Sync serial port
|
# Sync serial port
|
||||||
self._config.serial_port = self._port_var.get()
|
self._config.serial_port = self._port_var.get()
|
||||||
# Sync file paths from FileSelectors
|
# Sync file paths from FileSelectors
|
||||||
@@ -219,6 +221,17 @@ class MainWindow(ctk.CTk):
|
|||||||
if self._config:
|
if self._config:
|
||||||
self._config.erase_all = self._erase_cb_var.get()
|
self._config.erase_all = self._erase_cb_var.get()
|
||||||
|
|
||||||
|
def _browse_xilinx_path(self) -> None:
|
||||||
|
"""Open directory dialog to select Xilinx root folder."""
|
||||||
|
from tkinter import filedialog
|
||||||
|
path = filedialog.askdirectory(title="Select Xilinx Root Directory")
|
||||||
|
if path:
|
||||||
|
self._xilinx_path_var.set(path)
|
||||||
|
if self._config:
|
||||||
|
self._config.xilinx_path = path
|
||||||
|
# Re-check tools with new path
|
||||||
|
self._check_vitis()
|
||||||
|
|
||||||
# ── UI Construction ────────────────────────────────────────
|
# ── UI Construction ────────────────────────────────────────
|
||||||
|
|
||||||
def _build_ui(self) -> None:
|
def _build_ui(self) -> None:
|
||||||
@@ -418,7 +431,7 @@ class MainWindow(ctk.CTk):
|
|||||||
row=0, column=0, sticky="nsew",
|
row=0, column=0, sticky="nsew",
|
||||||
padx=PADDING, pady=PADDING,
|
padx=PADDING, pady=PADDING,
|
||||||
)
|
)
|
||||||
panel.grid_rowconfigure(7, weight=1)
|
panel.grid_rowconfigure(8, weight=1)
|
||||||
|
|
||||||
title = ctk.CTkLabel(
|
title = ctk.CTkLabel(
|
||||||
panel,
|
panel,
|
||||||
@@ -427,9 +440,23 @@ class MainWindow(ctk.CTk):
|
|||||||
)
|
)
|
||||||
title.grid(row=0, column=0, padx=PADDING, pady=PADDING)
|
title.grid(row=0, column=0, padx=PADDING, pady=PADDING)
|
||||||
|
|
||||||
|
# ── Xilinx Kit path ──
|
||||||
|
xilinx_frame = ctk.CTkFrame(panel)
|
||||||
|
xilinx_frame.grid(row=1, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||||
|
xilinx_frame.grid_columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
ctk.CTkLabel(xilinx_frame, text="Xilinx Kit:", font=FONT_BODY).grid(row=0, column=0, sticky="w")
|
||||||
|
self._xilinx_path_var = ctk.StringVar(value=self._config.xilinx_path)
|
||||||
|
self._xilinx_entry = ctk.CTkEntry(xilinx_frame, textvariable=self._xilinx_path_var)
|
||||||
|
self._xilinx_entry.grid(row=0, column=1, sticky="nsew", padx=PADDING_SMALL)
|
||||||
|
ctk.CTkButton(
|
||||||
|
xilinx_frame, text="Browse", font=FONT_SMALL, width=70,
|
||||||
|
command=self._browse_xilinx_path,
|
||||||
|
).grid(row=0, column=2, padx=(0, PADDING_SMALL))
|
||||||
|
|
||||||
# IP address
|
# IP address
|
||||||
ip_frame = ctk.CTkFrame(panel)
|
ip_frame = ctk.CTkFrame(panel)
|
||||||
ip_frame.grid(row=1, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
ip_frame.grid(row=2, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||||
ip_frame.grid_columnconfigure(1, weight=1)
|
ip_frame.grid_columnconfigure(1, weight=1)
|
||||||
|
|
||||||
ctk.CTkLabel(ip_frame, text="Zynq IP:", font=FONT_BODY).grid(row=0, column=0, sticky="w")
|
ctk.CTkLabel(ip_frame, text="Zynq IP:", font=FONT_BODY).grid(row=0, column=0, sticky="w")
|
||||||
@@ -446,7 +473,7 @@ class MainWindow(ctk.CTk):
|
|||||||
if self._config.bootloader_bit_path:
|
if self._config.bootloader_bit_path:
|
||||||
resolved = self._config.resolve_path(self._config.bootloader_bit_path)
|
resolved = self._config.resolve_path(self._config.bootloader_bit_path)
|
||||||
self._bit_selector.set_path(str(resolved))
|
self._bit_selector.set_path(str(resolved))
|
||||||
self._bit_selector.grid(row=2, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
self._bit_selector.grid(row=3, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||||
|
|
||||||
# ELF path (Bootloader)
|
# ELF path (Bootloader)
|
||||||
self._elf_selector = FileSelector(
|
self._elf_selector = FileSelector(
|
||||||
@@ -457,7 +484,7 @@ class MainWindow(ctk.CTk):
|
|||||||
if self._config.bootloader_elf_path:
|
if self._config.bootloader_elf_path:
|
||||||
resolved = self._config.resolve_path(self._config.bootloader_elf_path)
|
resolved = self._config.resolve_path(self._config.bootloader_elf_path)
|
||||||
self._elf_selector.set_path(str(resolved))
|
self._elf_selector.set_path(str(resolved))
|
||||||
self._elf_selector.grid(row=3, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
self._elf_selector.grid(row=4, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||||
|
|
||||||
# Bootloader BIN path (Flash programming)
|
# Bootloader BIN path (Flash programming)
|
||||||
self._bootloader_bin_selector = FileSelector(
|
self._bootloader_bin_selector = FileSelector(
|
||||||
@@ -468,7 +495,7 @@ class MainWindow(ctk.CTk):
|
|||||||
if self._config.bootloader_bin_path:
|
if self._config.bootloader_bin_path:
|
||||||
resolved = self._config.resolve_path(self._config.bootloader_bin_path)
|
resolved = self._config.resolve_path(self._config.bootloader_bin_path)
|
||||||
self._bootloader_bin_selector.set_path(str(resolved))
|
self._bootloader_bin_selector.set_path(str(resolved))
|
||||||
self._bootloader_bin_selector.grid(row=4, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
self._bootloader_bin_selector.grid(row=5, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||||
|
|
||||||
# FSBL ELF path (required by program_flash for QSPI programming)
|
# FSBL ELF path (required by program_flash for QSPI programming)
|
||||||
self._fsbl_selector = FileSelector(
|
self._fsbl_selector = FileSelector(
|
||||||
@@ -479,7 +506,7 @@ class MainWindow(ctk.CTk):
|
|||||||
if self._config.fsbl_elf_path:
|
if self._config.fsbl_elf_path:
|
||||||
resolved = self._config.resolve_path(self._config.fsbl_elf_path)
|
resolved = self._config.resolve_path(self._config.fsbl_elf_path)
|
||||||
self._fsbl_selector.set_path(str(resolved))
|
self._fsbl_selector.set_path(str(resolved))
|
||||||
self._fsbl_selector.grid(row=5, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
self._fsbl_selector.grid(row=6, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||||
|
|
||||||
# Flash options
|
# Flash options
|
||||||
flash_frame = ctk.CTkFrame(panel)
|
flash_frame = ctk.CTkFrame(panel)
|
||||||
@@ -512,7 +539,7 @@ class MainWindow(ctk.CTk):
|
|||||||
if self._config.firmware_bin_path:
|
if self._config.firmware_bin_path:
|
||||||
resolved = self._config.resolve_path(self._config.firmware_bin_path)
|
resolved = self._config.resolve_path(self._config.firmware_bin_path)
|
||||||
self._firmware_bin_selector.set_path(str(resolved))
|
self._firmware_bin_selector.set_path(str(resolved))
|
||||||
self._firmware_bin_selector.grid(row=7, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
self._firmware_bin_selector.grid(row=8, column=0, sticky="nsew", padx=PADDING, pady=PADDING_SMALL)
|
||||||
|
|
||||||
# Save button
|
# Save button
|
||||||
save_btn = ctk.CTkButton(
|
save_btn = ctk.CTkButton(
|
||||||
@@ -521,7 +548,7 @@ class MainWindow(ctk.CTk):
|
|||||||
font=FONT_SMALL,
|
font=FONT_SMALL,
|
||||||
command=self._save_config,
|
command=self._save_config,
|
||||||
)
|
)
|
||||||
save_btn.grid(row=8, column=0, padx=PADDING, pady=PADDING)
|
save_btn.grid(row=9, column=0, padx=PADDING, pady=PADDING)
|
||||||
|
|
||||||
def _build_serial_panel(self, parent: ctk.CTkFrame) -> None:
|
def _build_serial_panel(self, parent: ctk.CTkFrame) -> None:
|
||||||
"""Build the serial monitor panel."""
|
"""Build the serial monitor panel."""
|
||||||
|
|||||||
+73
-52
@@ -52,86 +52,117 @@ TOOL_ALIASES: dict[str, list[str]] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _find_executable(exec_name: str, vitis_path: str, system: str) -> str | None:
|
def _find_executable(
|
||||||
"""Find an executable by name using PATH and Vitis search paths.
|
exec_name: str,
|
||||||
|
vitis_path: str,
|
||||||
|
system: str,
|
||||||
|
xilinx_root: str = "",
|
||||||
|
) -> str | None:
|
||||||
|
"""Find an executable by name using PATH, Vitis, and Xilinx root.
|
||||||
|
|
||||||
|
Search order:
|
||||||
|
1. System PATH (shutil.which)
|
||||||
|
2. vitis_path/bin (legacy config)
|
||||||
|
3. xilinx_root/Vitis/*/bin and Vivado/*/bin (multi-version scan)
|
||||||
|
4. Common install paths
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
exec_name: Executable name (without extension).
|
exec_name: Executable name (without extension).
|
||||||
vitis_path: Vitis/Vivado installation root directory.
|
vitis_path: Legacy Vitis installation directory.
|
||||||
system: Platform identifier (linux, windows, darwin).
|
system: Platform identifier (linux, windows, darwin).
|
||||||
|
xilinx_root: Xilinx root directory (e.g., /data/xilinx or C:/Xilinx).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Absolute path to the executable, or None.
|
Absolute path to the executable, or None.
|
||||||
"""
|
"""
|
||||||
exec_suffix = ".exe" if system == "windows" else ""
|
exec_suffix = ".exe" if system == "windows" else ""
|
||||||
|
|
||||||
# 1. shutil.which() — most reliable cross-platform PATH resolution
|
# 1. System PATH
|
||||||
found = shutil.which(f"{exec_name}{exec_suffix}")
|
found = shutil.which(f"{exec_name}{exec_suffix}")
|
||||||
if found:
|
if found:
|
||||||
return found
|
return found
|
||||||
|
|
||||||
# 2. Scan vitis_path/bin
|
# 2. Legacy vitis_path/bin
|
||||||
if vitis_path:
|
if vitis_path:
|
||||||
candidate = Path(vitis_path) / "bin" / f"{exec_name}{exec_suffix}"
|
candidate = Path(vitis_path) / "bin" / f"{exec_name}{exec_suffix}"
|
||||||
if candidate.is_file():
|
if candidate.is_file():
|
||||||
return str(candidate)
|
return str(candidate)
|
||||||
|
|
||||||
# 3. Common Xilinx install paths
|
# 3. Scan xilinx_root for all versions (Vitis + Vivado)
|
||||||
common_paths = [
|
if xilinx_root:
|
||||||
f"/data/xilinx/Vitis/2023.2/bin/{exec_name}",
|
xr = Path(xilinx_root)
|
||||||
f"/data/xilinx/Vivado/2023.2/bin/{exec_name}",
|
for product in ("Vitis", "Vivado"):
|
||||||
f"/opt/xilinx/bin/{exec_name}",
|
pdir = xr / product
|
||||||
os.path.expanduser(f"~/Xilinx/Vitis/2023.2/bin/{exec_name}"),
|
if not pdir.is_dir():
|
||||||
os.path.expanduser(f"~/Xilinx/Vivado/2023.2/bin/{exec_name}"),
|
continue
|
||||||
]
|
# Scan version directories, newest first
|
||||||
for p in common_paths:
|
versions = sorted(
|
||||||
|
[d for d in pdir.iterdir() if d.is_dir()],
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
for ver_dir in versions:
|
||||||
|
candidate = ver_dir / "bin" / f"{exec_name}{exec_suffix}"
|
||||||
|
if candidate.is_file():
|
||||||
|
return str(candidate)
|
||||||
|
|
||||||
|
# 4. Common install paths (cross-platform)
|
||||||
|
if system == "windows":
|
||||||
|
common = [
|
||||||
|
f"C:/Xilinx/Vitis/2023.2/bin/{exec_name}.exe",
|
||||||
|
f"C:/Xilinx/Vivado/2023.2/bin/{exec_name}.exe",
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
common = [
|
||||||
|
f"/data/xilinx/Vitis/2023.2/bin/{exec_name}",
|
||||||
|
f"/data/xilinx/Vivado/2023.2/bin/{exec_name}",
|
||||||
|
f"/opt/xilinx/bin/{exec_name}",
|
||||||
|
os.path.expanduser(f"~/Xilinx/Vitis/2023.2/bin/{exec_name}"),
|
||||||
|
os.path.expanduser(f"~/Xilinx/Vivado/2023.2/bin/{exec_name}"),
|
||||||
|
]
|
||||||
|
for p in common:
|
||||||
if os.path.isfile(p):
|
if os.path.isfile(p):
|
||||||
return p
|
return p
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_xsct_path(vitis_path: str = "") -> str | None:
|
def get_xsct_path(vitis_path: str = "", xilinx_root: str = "") -> str | None:
|
||||||
"""Find the xsct (Xilinx Software Command Line Tool) executable.
|
"""Find the xsct (Xilinx Software Command Line Tool) executable.
|
||||||
|
|
||||||
xsct is the primary JTAG scripting tool for Vivado/Vitis (2015.x+).
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vitis_path: Optional Vitis installation directory.
|
vitis_path: Legacy Vitis install path.
|
||||||
|
xilinx_root: Xilinx root directory.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Absolute path to xsct, or None.
|
Absolute path to xsct, or None.
|
||||||
"""
|
"""
|
||||||
return _find_executable("xsct", vitis_path, platform.system().lower())
|
return _find_executable("xsct", vitis_path, platform.system().lower(), xilinx_root)
|
||||||
|
|
||||||
|
|
||||||
def get_program_flash_path(vitis_path: str = "") -> str | None:
|
def get_program_flash_path(vitis_path: str = "", xilinx_root: str = "") -> str | None:
|
||||||
"""Find the program_flash executable (QSPI flash programming).
|
"""Find the program_flash executable.
|
||||||
|
|
||||||
program_flash is part of Vitis/SDK (2014.x+).
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vitis_path: Optional Vitis installation directory.
|
vitis_path: Legacy Vitis install path.
|
||||||
|
xilinx_root: Xilinx root directory.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Absolute path to program_flash, or None.
|
Absolute path to program_flash, or None.
|
||||||
"""
|
"""
|
||||||
return _find_executable("program_flash", vitis_path, platform.system().lower())
|
return _find_executable("program_flash", vitis_path, platform.system().lower(), xilinx_root)
|
||||||
|
|
||||||
|
|
||||||
def get_bootgen_path(vitis_path: str = "") -> str | None:
|
def get_bootgen_path(vitis_path: str = "", xilinx_root: str = "") -> str | None:
|
||||||
"""Find the bootgen executable (BOOT.BIN generation).
|
"""Find the bootgen executable.
|
||||||
|
|
||||||
bootgen creates boot images from BIF descriptions. It does NOT
|
|
||||||
program flash — use program_flash for that.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
vitis_path: Optional Vitis installation directory.
|
vitis_path: Legacy Vitis install path.
|
||||||
|
xilinx_root: Xilinx root directory.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Absolute path to bootgen, or None.
|
Absolute path to bootgen, or None.
|
||||||
"""
|
"""
|
||||||
return _find_executable("bootgen", vitis_path, platform.system().lower())
|
return _find_executable("bootgen", vitis_path, platform.system().lower(), xilinx_root)
|
||||||
|
|
||||||
|
|
||||||
def _get_tool_version(tool_path: str, tool_name: str) -> str:
|
def _get_tool_version(tool_path: str, tool_name: str) -> str:
|
||||||
@@ -162,8 +193,6 @@ def _get_tool_version(tool_path: str, tool_name: str) -> str:
|
|||||||
def check_vitis(config: Config) -> VitisCheckResult:
|
def check_vitis(config: Config) -> VitisCheckResult:
|
||||||
"""Check availability of all required Xilinx tools.
|
"""Check availability of all required Xilinx tools.
|
||||||
|
|
||||||
Scans PATH and Vitis paths for: xsct, program_flash, bootgen.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config: Application configuration.
|
config: Application configuration.
|
||||||
|
|
||||||
@@ -171,19 +200,20 @@ def check_vitis(config: Config) -> VitisCheckResult:
|
|||||||
VitisCheckResult with per-tool status.
|
VitisCheckResult with per-tool status.
|
||||||
"""
|
"""
|
||||||
system = platform.system().lower()
|
system = platform.system().lower()
|
||||||
vitis_path = config.vitis_path or ""
|
vitis_path = config.vitis_path if hasattr(config, 'vitis_path') else ""
|
||||||
|
xilinx_root = config.xilinx_path if hasattr(config, 'xilinx_path') else ""
|
||||||
tools: dict[str, ToolStatus] = {}
|
tools: dict[str, ToolStatus] = {}
|
||||||
errors: list[str] = []
|
errors: list[str] = []
|
||||||
|
|
||||||
for tool_name, aliases in TOOL_ALIASES.items():
|
for tool_name, aliases in TOOL_ALIASES.items():
|
||||||
status = _check_tool(tool_name, aliases, vitis_path, system)
|
status = _check_tool(tool_name, aliases, vitis_path, system, xilinx_root)
|
||||||
tools[tool_name] = status
|
tools[tool_name] = status
|
||||||
if not status.found:
|
if not status.found:
|
||||||
errors.append(f"{tool_name}: {status.error or 'not found'}")
|
errors.append(f"{tool_name}: {status.error or 'not found'}")
|
||||||
|
|
||||||
return VitisCheckResult(
|
return VitisCheckResult(
|
||||||
system=system,
|
system=system,
|
||||||
vitis_path=vitis_path,
|
vitis_path=xilinx_root or vitis_path,
|
||||||
tools=tools,
|
tools=tools,
|
||||||
is_ready=len(errors) == 0,
|
is_ready=len(errors) == 0,
|
||||||
errors=errors,
|
errors=errors,
|
||||||
@@ -195,20 +225,11 @@ def _check_tool(
|
|||||||
aliases: list[str],
|
aliases: list[str],
|
||||||
vitis_path: str,
|
vitis_path: str,
|
||||||
system: str,
|
system: str,
|
||||||
|
xilinx_root: str = "",
|
||||||
) -> ToolStatus:
|
) -> ToolStatus:
|
||||||
"""Check if a single tool is available.
|
"""Check if a single tool is available."""
|
||||||
|
|
||||||
Args:
|
|
||||||
tool_name: Canonical tool name.
|
|
||||||
aliases: Executable names to try.
|
|
||||||
vitis_path: Vitis installation path.
|
|
||||||
system: Platform identifier.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ToolStatus with detection results.
|
|
||||||
"""
|
|
||||||
for alias in aliases:
|
for alias in aliases:
|
||||||
found = _find_executable(alias, vitis_path, system)
|
found = _find_executable(alias, vitis_path, system, xilinx_root)
|
||||||
if found:
|
if found:
|
||||||
version = _get_tool_version(found, tool_name)
|
version = _get_tool_version(found, tool_name)
|
||||||
return ToolStatus(
|
return ToolStatus(
|
||||||
@@ -223,9 +244,9 @@ def _check_tool(
|
|||||||
name=tool_name,
|
name=tool_name,
|
||||||
found=False,
|
found=False,
|
||||||
error=(
|
error=(
|
||||||
f"Could not locate {', '.join(aliases)} in PATH or {vitis_path}/bin"
|
f"Could not locate {', '.join(aliases)} in PATH"
|
||||||
if vitis_path
|
+ (f" or {xilinx_root}" if xilinx_root else "")
|
||||||
else f"Could not locate {', '.join(aliases)} in PATH"
|
+ (f" or {vitis_path}" if vitis_path else "")
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user