fix: remove manual hw_server management — let Vivado handle it internally
External hw_server start via Popen was unreliable on Windows (Vivado has its own hw_server lifecycle via open_hw_manager). Simplified to just run Vivado TCL directly — tested working on Linux (6.9s). Also bumped default check_env timeout from 120s to 180s for Windows where Vivado cold-start can take significantly longer. Removed: _port_open, socket import, manual hw_server Popen/kill logic.
This commit is contained in:
@@ -35,7 +35,7 @@ DEFAULT_CONFIG: dict[str, Any] = {
|
|||||||
"inter_step_delay": 2,
|
"inter_step_delay": 2,
|
||||||
"boot_wait_delay": 10,
|
"boot_wait_delay": 10,
|
||||||
"step_timeouts": {
|
"step_timeouts": {
|
||||||
"check_env": 120,
|
"check_env": 180,
|
||||||
"flash_erase": 120,
|
"flash_erase": 120,
|
||||||
"flash_program": 600,
|
"flash_program": 600,
|
||||||
"bitstream": 60,
|
"bitstream": 60,
|
||||||
|
|||||||
+6
-70
@@ -7,16 +7,14 @@ returning device names, IDCODEs, and PS/PL presence.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import socket
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
|
||||||
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, get_hw_server_path, build_tool_command
|
from vitis_checker import get_vivado_path, build_tool_command
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -68,17 +66,6 @@ _ARM_DAP_RE = re.compile(r"^arm_dap", re.IGNORECASE)
|
|||||||
HW_SERVER_PORT = 3121
|
HW_SERVER_PORT = 3121
|
||||||
|
|
||||||
|
|
||||||
# ── Helpers ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
def _port_open(host: str = "localhost", port: int = HW_SERVER_PORT) -> bool:
|
|
||||||
"""Check if a TCP port is accepting connections."""
|
|
||||||
try:
|
|
||||||
with socket.create_connection((host, port), timeout=1):
|
|
||||||
return True
|
|
||||||
except OSError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _idcode_to_hex(binary_str: str) -> str:
|
def _idcode_to_hex(binary_str: str) -> str:
|
||||||
"""Convert Vivado's binary IDCODE to uppercase hex."""
|
"""Convert Vivado's binary IDCODE to uppercase hex."""
|
||||||
try:
|
try:
|
||||||
@@ -94,10 +81,9 @@ def check_zynq_jtag(
|
|||||||
) -> ZynqCheckResult:
|
) -> ZynqCheckResult:
|
||||||
"""Check for Zynq devices on the JTAG chain via Vivado + hw_server.
|
"""Check for Zynq devices on the JTAG chain via Vivado + hw_server.
|
||||||
|
|
||||||
Lifecycle:
|
Vivado manages hw_server internally — open_hw_manager auto-starts
|
||||||
1. If hw_server is already running (port 3121 open) → reuse it
|
the hardware server and connect_hw_server connects to it. No manual
|
||||||
2. If not running → start it, kill it when done
|
hw_server lifecycle needed.
|
||||||
3. Run Vivado batch TCL to enumerate JTAG devices
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config: Application configuration.
|
config: Application configuration.
|
||||||
@@ -112,7 +98,7 @@ def check_zynq_jtag(
|
|||||||
if callback:
|
if callback:
|
||||||
callback("start", "Connecting to hw_server...")
|
callback("start", "Connecting to hw_server...")
|
||||||
|
|
||||||
# ── 1. Find tools ──────────────────────────────────────────
|
# ── 1. Find Vivado ──────────────────────────────────────────
|
||||||
vivado_path = get_vivado_path(xilinx_root=xilinx_root)
|
vivado_path = get_vivado_path(xilinx_root=xilinx_root)
|
||||||
if not vivado_path:
|
if not vivado_path:
|
||||||
return ZynqCheckResult(
|
return ZynqCheckResult(
|
||||||
@@ -122,46 +108,7 @@ def check_zynq_jtag(
|
|||||||
error="Vivado not found",
|
error="Vivado not found",
|
||||||
)
|
)
|
||||||
|
|
||||||
hw_server_path = get_hw_server_path(xilinx_root=xilinx_root)
|
# ── 2. Run Vivado TCL (manages hw_server internally) ────────
|
||||||
hw_server_was_running = _port_open()
|
|
||||||
|
|
||||||
# ── 2. Start hw_server if needed ───────────────────────────
|
|
||||||
hw_proc: subprocess.Popen | None = None
|
|
||||||
if not hw_server_was_running:
|
|
||||||
if not hw_server_path:
|
|
||||||
return ZynqCheckResult(
|
|
||||||
devices=[], jtag_chain=[], is_present=False,
|
|
||||||
ps_detected=False, pl_detected=False,
|
|
||||||
hw_server_url=hw_server_url,
|
|
||||||
error="hw_server not found — install Vivado/Vitis",
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
hw_proc = subprocess.Popen(
|
|
||||||
build_tool_command(hw_server_path),
|
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.DEVNULL,
|
|
||||||
)
|
|
||||||
# Wait for port to become available
|
|
||||||
for _ in range(30): # 3s max
|
|
||||||
if _port_open():
|
|
||||||
break
|
|
||||||
time.sleep(0.1)
|
|
||||||
else:
|
|
||||||
return ZynqCheckResult(
|
|
||||||
devices=[], jtag_chain=[], is_present=False,
|
|
||||||
ps_detected=False, pl_detected=False,
|
|
||||||
hw_server_url=hw_server_url,
|
|
||||||
error="hw_server started but port 3121 not open",
|
|
||||||
)
|
|
||||||
except OSError as e:
|
|
||||||
return ZynqCheckResult(
|
|
||||||
devices=[], jtag_chain=[], is_present=False,
|
|
||||||
ps_detected=False, pl_detected=False,
|
|
||||||
hw_server_url=hw_server_url,
|
|
||||||
error=f"Failed to start hw_server: {e}",
|
|
||||||
)
|
|
||||||
|
|
||||||
# ── 3. Run Vivado TCL ──────────────────────────────────────
|
|
||||||
try:
|
try:
|
||||||
tcl = _build_jtag_query_tcl()
|
tcl = _build_jtag_query_tcl()
|
||||||
if callback:
|
if callback:
|
||||||
@@ -216,17 +163,6 @@ def check_zynq_jtag(
|
|||||||
hw_server_url=hw_server_url,
|
hw_server_url=hw_server_url,
|
||||||
error=f"OS error: {e}",
|
error=f"OS error: {e}",
|
||||||
)
|
)
|
||||||
finally:
|
|
||||||
# ── 4. Kill hw_server only if WE started it ────────────
|
|
||||||
if hw_proc is not None and hw_proc.poll() is None:
|
|
||||||
try:
|
|
||||||
hw_proc.terminate()
|
|
||||||
hw_proc.wait(timeout=3)
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
hw_proc.kill()
|
|
||||||
hw_proc.wait()
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# ── TCL builder ────────────────────────────────────────────────────────
|
# ── TCL builder ────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user