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:
2026-06-11 17:09:21 +08:00
parent 954aed7c0d
commit 743703c79e
2 changed files with 7 additions and 71 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ DEFAULT_CONFIG: dict[str, Any] = {
"inter_step_delay": 2,
"boot_wait_delay": 10,
"step_timeouts": {
"check_env": 120,
"check_env": 180,
"flash_erase": 120,
"flash_program": 600,
"bitstream": 60,
+6 -70
View File
@@ -7,16 +7,14 @@ returning device names, IDCODEs, and PS/PL presence.
from __future__ import annotations
import re
import socket
import subprocess
import tempfile
import time
from dataclasses import dataclass, field
from pathlib import Path
from typing import Callable
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
@@ -68,17 +66,6 @@ _ARM_DAP_RE = re.compile(r"^arm_dap", re.IGNORECASE)
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:
"""Convert Vivado's binary IDCODE to uppercase hex."""
try:
@@ -94,10 +81,9 @@ def check_zynq_jtag(
) -> ZynqCheckResult:
"""Check for Zynq devices on the JTAG chain via Vivado + hw_server.
Lifecycle:
1. If hw_server is already running (port 3121 open) → reuse it
2. If not running → start it, kill it when done
3. Run Vivado batch TCL to enumerate JTAG devices
Vivado manages hw_server internally — open_hw_manager auto-starts
the hardware server and connect_hw_server connects to it. No manual
hw_server lifecycle needed.
Args:
config: Application configuration.
@@ -112,7 +98,7 @@ def check_zynq_jtag(
if callback:
callback("start", "Connecting to hw_server...")
# ── 1. Find tools ──────────────────────────────────────────
# ── 1. Find Vivado ──────────────────────────────────────────
vivado_path = get_vivado_path(xilinx_root=xilinx_root)
if not vivado_path:
return ZynqCheckResult(
@@ -122,46 +108,7 @@ def check_zynq_jtag(
error="Vivado not found",
)
hw_server_path = get_hw_server_path(xilinx_root=xilinx_root)
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 ──────────────────────────────────────
# ── 2. Run Vivado TCL (manages hw_server internally) ────────
try:
tcl = _build_jtag_query_tcl()
if callback:
@@ -216,17 +163,6 @@ def check_zynq_jtag(
hw_server_url=hw_server_url,
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 ────────────────────────────────────────────────────────