Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17c8f2d68b | ||
|
|
61f0e6cd2b | ||
|
|
6d686964e2 |
@@ -11,14 +11,13 @@ Compatible with Xilinx 2018.3 — 2023.2.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from config_manager import Config
|
from config_manager import Config
|
||||||
from vitis_checker import get_xsct_path
|
from vitis_checker import get_xsct_path, get_vivado_path
|
||||||
|
|
||||||
# ── Types ───────────────────────────────────────────────────────────
|
# ── Types ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -38,35 +37,6 @@ ProgressCallback = Callable[[str, str], None] | None
|
|||||||
# ── Helpers ─────────────────────────────────────────────────────────
|
# ── Helpers ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
def _get_vivado_path(vitis_path: str) -> str | None:
|
|
||||||
"""Find the Vivado executable.
|
|
||||||
|
|
||||||
Uses shutil.which() for PATH resolution, then common install paths.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
vitis_path: Optional Vitis installation directory.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Path to vivado executable, or None.
|
|
||||||
"""
|
|
||||||
found = shutil.which("vivado")
|
|
||||||
if found:
|
|
||||||
return found
|
|
||||||
if vitis_path:
|
|
||||||
candidate = Path(vitis_path) / "bin" / "vivado"
|
|
||||||
if candidate.is_file():
|
|
||||||
return str(candidate)
|
|
||||||
common = [
|
|
||||||
"/data/xilinx/Vivado/2023.2/bin/vivado",
|
|
||||||
"/opt/xilinx/bin/vivado",
|
|
||||||
os.path.expanduser("~/Xilinx/Vivado/2023.2/bin/vivado"),
|
|
||||||
]
|
|
||||||
for p in common:
|
|
||||||
if os.path.isfile(p):
|
|
||||||
return p
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _find_ps7_init_tcl(config: Config) -> str:
|
def _find_ps7_init_tcl(config: Config) -> str:
|
||||||
"""Find the ps7_init.tcl file for PS initialization.
|
"""Find the ps7_init.tcl file for PS initialization.
|
||||||
|
|
||||||
@@ -167,16 +137,15 @@ def program_bitstream(
|
|||||||
|
|
||||||
# Try xsct first (preferred)
|
# Try xsct first (preferred)
|
||||||
xsct = get_xsct_path(
|
xsct = get_xsct_path(
|
||||||
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
xilinx_root=getattr(config, "xilinx_path", ""),
|
||||||
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)
|
||||||
|
|
||||||
# Fallback to Vivado
|
# Fallback to Vivado
|
||||||
vivado = _get_vivado_path(
|
vivado = get_vivado_path(
|
||||||
config.vitis_path if hasattr(config, 'vitis_path') else ""
|
xilinx_root=getattr(config, "xilinx_path", ""),
|
||||||
)
|
)
|
||||||
if vivado:
|
if vivado:
|
||||||
return _program_with_vivado(vivado, bit_path, callback)
|
return _program_with_vivado(vivado, bit_path, callback)
|
||||||
@@ -364,8 +333,7 @@ def run_elf(
|
|||||||
callback("start", f"Running ELF: {elf_path.name}...")
|
callback("start", f"Running ELF: {elf_path.name}...")
|
||||||
|
|
||||||
xsct = get_xsct_path(
|
xsct = get_xsct_path(
|
||||||
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
xilinx_root=getattr(config, "xilinx_path", ""),
|
||||||
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
|
||||||
)
|
)
|
||||||
if not xsct:
|
if not xsct:
|
||||||
return BitstreamResult(
|
return BitstreamResult(
|
||||||
@@ -579,8 +547,7 @@ def full_bitstream_program(
|
|||||||
message=f"FSBL not found: {fsbl_path or '(not configured)'}")]
|
message=f"FSBL not found: {fsbl_path or '(not configured)'}")]
|
||||||
|
|
||||||
xsct = get_xsct_path(
|
xsct = get_xsct_path(
|
||||||
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
xilinx_root=getattr(config, "xilinx_path", ""),
|
||||||
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
|
||||||
)
|
)
|
||||||
if not xsct:
|
if not xsct:
|
||||||
return [BitstreamResult(step="bitstream", success=False,
|
return [BitstreamResult(step="bitstream", success=False,
|
||||||
|
|||||||
@@ -113,8 +113,7 @@ def wipe_flash(
|
|||||||
FlashResult with status.
|
FlashResult with status.
|
||||||
"""
|
"""
|
||||||
flash_tool = get_program_flash_path(
|
flash_tool = get_program_flash_path(
|
||||||
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
xilinx_root=getattr(config, "xilinx_path", ""),
|
||||||
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
|
||||||
)
|
)
|
||||||
if not flash_tool:
|
if not flash_tool:
|
||||||
return FlashResult(
|
return FlashResult(
|
||||||
@@ -229,8 +228,7 @@ def program_flash_bin(
|
|||||||
)
|
)
|
||||||
|
|
||||||
flash_tool = get_program_flash_path(
|
flash_tool = get_program_flash_path(
|
||||||
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
xilinx_root=getattr(config, "xilinx_path", ""),
|
||||||
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
|
||||||
)
|
)
|
||||||
if not flash_tool:
|
if not flash_tool:
|
||||||
return FlashResult(
|
return FlashResult(
|
||||||
|
|||||||
+13
-5
@@ -1144,15 +1144,23 @@ class MainWindow(ctk.CTk):
|
|||||||
|
|
||||||
# Wait for board to boot — UART if available, else config delay
|
# Wait for board to boot — UART if available, else config delay
|
||||||
boot_delay = self._config.boot_wait_delay
|
boot_delay = self._config.boot_wait_delay
|
||||||
uart_alive = (self._uart_monitor is not None
|
# Check both main monitor and UART window monitor
|
||||||
and self._uart_monitor.is_running())
|
uart_alive = False
|
||||||
|
_uart_src = None # source of latest_info
|
||||||
|
if self._uart_monitor and self._uart_monitor.is_running():
|
||||||
|
uart_alive = True
|
||||||
|
_uart_src = self._uart_monitor
|
||||||
|
elif (self._uart_window and self._uart_window.winfo_exists()
|
||||||
|
and self._uart_window.is_monitoring):
|
||||||
|
uart_alive = True
|
||||||
|
_uart_src = self._uart_window
|
||||||
discovered_ip: str = ""
|
discovered_ip: str = ""
|
||||||
if uart_alive:
|
if uart_alive and _uart_src:
|
||||||
self._log_message(f" ⏳ Waiting for UART boot signal (max {boot_delay}s)...")
|
self._log_message(f" ⏳ Waiting for UART boot signal (max {boot_delay}s)...")
|
||||||
elapsed = 0.0
|
elapsed = 0.0
|
||||||
while elapsed < boot_delay:
|
while elapsed < boot_delay:
|
||||||
info = self._uart_monitor.latest_info
|
info = _uart_src.latest_info if hasattr(_uart_src, 'latest_info') else _uart_src.get_latest_info()
|
||||||
if info.ip_address or info.version:
|
if info and (info.ip_address or info.version):
|
||||||
self._log_message(
|
self._log_message(
|
||||||
f" ✓ Boot detected after {elapsed:.1f}s"
|
f" ✓ Boot detected after {elapsed:.1f}s"
|
||||||
f" (IP={info.ip_address}, Ver={info.version})"
|
f" (IP={info.ip_address}, Ver={info.version})"
|
||||||
|
|||||||
@@ -1190,6 +1190,12 @@ class UartMonitorWindow(ctk.CTkToplevel):
|
|||||||
"""True if the UART monitor thread is currently running."""
|
"""True if the UART monitor thread is currently running."""
|
||||||
return self._monitor is not None and self._monitor.is_running()
|
return self._monitor is not None and self._monitor.is_running()
|
||||||
|
|
||||||
|
def get_latest_info(self):
|
||||||
|
"""Return latest parsed BootInfo from the UART monitor, or None."""
|
||||||
|
if self._monitor:
|
||||||
|
return self._monitor.latest_info
|
||||||
|
return None
|
||||||
|
|
||||||
def _on_close(self) -> None:
|
def _on_close(self) -> None:
|
||||||
"""Cleanup UartMonitor and destroy window."""
|
"""Cleanup UartMonitor and destroy window."""
|
||||||
if self._monitor:
|
if self._monitor:
|
||||||
|
|||||||
@@ -186,8 +186,7 @@ def reboot_via_jtag(
|
|||||||
from vitis_checker import get_xsct_path
|
from vitis_checker import get_xsct_path
|
||||||
|
|
||||||
xsct = get_xsct_path(
|
xsct = get_xsct_path(
|
||||||
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
xilinx_root=getattr(config, "xilinx_path", ""),
|
||||||
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
|
||||||
)
|
)
|
||||||
if not xsct:
|
if not xsct:
|
||||||
return RebootResult(
|
return RebootResult(
|
||||||
|
|||||||
@@ -317,6 +317,19 @@ def get_bootgen_path(vitis_path: str = "", xilinx_root: str = "") -> str | None:
|
|||||||
return _find_executable("bootgen", vitis_path, platform.system().lower(), xilinx_root)
|
return _find_executable("bootgen", vitis_path, platform.system().lower(), xilinx_root)
|
||||||
|
|
||||||
|
|
||||||
|
def get_vivado_path(vitis_path: str = "", xilinx_root: str = "") -> str | None:
|
||||||
|
"""Find the Vivado executable (JTAG operations via hw_server).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
vitis_path: Legacy Vitis installation directory.
|
||||||
|
xilinx_root: Xilinx root directory.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path to vivado executable, or None.
|
||||||
|
"""
|
||||||
|
return _find_executable("vivado", vitis_path, platform.system().lower(), xilinx_root)
|
||||||
|
|
||||||
|
|
||||||
# ── Tool version detection ───────────────────────────────────────────
|
# ── Tool version detection ───────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+7
-35
@@ -10,13 +10,13 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from config_manager import Config
|
from config_manager import Config
|
||||||
|
from vitis_checker import get_vivado_path, build_tool_command
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -86,9 +86,10 @@ def check_zynq_jtag(config: Config, callback: Callable | None = None) -> ZynqChe
|
|||||||
if callback:
|
if callback:
|
||||||
callback("start", "Connecting to hw_server...")
|
callback("start", "Connecting to hw_server...")
|
||||||
|
|
||||||
# Find vivado executable
|
# Find vivado executable via Xilinx root scan
|
||||||
vivado_path = _get_vivado_path(
|
vivado_path = get_vivado_path(
|
||||||
config.vitis_path if hasattr(config, 'vitis_path') else ""
|
vitis_path=getattr(config, "vitis_path", ""),
|
||||||
|
xilinx_root=getattr(config, "xilinx_path", ""),
|
||||||
)
|
)
|
||||||
if not vivado_path:
|
if not vivado_path:
|
||||||
return ZynqCheckResult(
|
return ZynqCheckResult(
|
||||||
@@ -110,8 +111,8 @@ def check_zynq_jtag(config: Config, callback: Callable | None = None) -> ZynqChe
|
|||||||
if callback:
|
if callback:
|
||||||
callback("progress", "Scanning JTAG chain...")
|
callback("progress", "Scanning JTAG chain...")
|
||||||
|
|
||||||
# Run Vivado in batch mode
|
# Run Vivado in batch mode (with environment setup)
|
||||||
cmd = [vivado_path, "-mode", "batch", "-source", str(tcl_path)]
|
cmd = build_tool_command(vivado_path, "-mode", "batch", "-source", str(tcl_path))
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
cmd,
|
cmd,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
@@ -322,35 +323,6 @@ def _parse_zynq_device(device_name: str) -> ZynqDevice | None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _get_vivado_path(vitis_path: str) -> str | None:
|
|
||||||
"""Find the Vivado executable path.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
vitis_path: Vitis installation path.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Path to Vivado executable, or None if not found.
|
|
||||||
"""
|
|
||||||
path = shutil.which("vivado")
|
|
||||||
if path:
|
|
||||||
return path
|
|
||||||
if vitis_path:
|
|
||||||
candidate = Path(vitis_path) / "bin" / "vivado"
|
|
||||||
if candidate.exists():
|
|
||||||
return str(candidate)
|
|
||||||
candidate = Path(vitis_path) / "bin" / "vivado.exe"
|
|
||||||
if candidate.exists():
|
|
||||||
return str(candidate)
|
|
||||||
common_paths = [
|
|
||||||
"/opt/xilinx/bin/vivado",
|
|
||||||
os.path.expanduser("~/Xilinx/Vivado/bin/vivado"),
|
|
||||||
]
|
|
||||||
for p in common_paths:
|
|
||||||
if os.path.isfile(p):
|
|
||||||
return p
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_zynq_status_dict(result: ZynqCheckResult) -> dict:
|
def get_zynq_status_dict(result: ZynqCheckResult) -> dict:
|
||||||
"""Convert a ZynqCheckResult to a serializable dictionary.
|
"""Convert a ZynqCheckResult to a serializable dictionary.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user