✨ feat: UART monitor, step timeouts, and non-blocking subprocess output
Based on real hardware timing data from XC7Z100 testing: UART Monitoring: - New UartMonitor class in serial_monitor.py: background thread reads serial output continuously, parses for IP/version/boot messages - GUI starts monitor when workflow runs, stops on completion/close - Serial lines flow into log display in real time Step Timing: - New step_timeouts config (experience × 2): check_env:30s flash_erase:120s flash_program:600s bitstream:60s elf_download:60s tftp_reboot:120s - GUI shows estimated timeout before each step - Shows actual elapsed time after each step completes - All subprocess calls now use config-driven timeouts Non-blocking Output: - New subprocess_utils.py: run_streaming() uses Popen + threaded stdout/stderr readers, parses WARNING/ERROR/progress% in real time - flash_programmer and bitstream_programmer use it - Callbacks receive categorized events (out/err/warn/error/progress/done)
This commit is contained in:
+17
-16
@@ -117,21 +117,17 @@ def _run_command(
|
||||
Raises:
|
||||
subprocess.TimeoutExpired: If command exceeds timeout.
|
||||
"""
|
||||
from subprocess_utils import run_streaming
|
||||
|
||||
if callback:
|
||||
callback("running", f"Executing: {' '.join(cmd)}")
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
if callback:
|
||||
if result.returncode == 0:
|
||||
callback("complete", f"{cmd[0]} succeeded")
|
||||
else:
|
||||
callback("error", f"{cmd[0]} failed (exit {result.returncode})")
|
||||
return result
|
||||
try:
|
||||
return run_streaming(cmd, timeout=timeout, callback=callback)
|
||||
except subprocess.TimeoutExpired:
|
||||
if callback:
|
||||
callback("error", f"{cmd[0]} timed out after {timeout}s")
|
||||
raise
|
||||
|
||||
|
||||
# ── Bitstream Download ──────────────────────────────────────────────
|
||||
@@ -172,7 +168,8 @@ def program_bitstream(
|
||||
# Try xsct first (preferred)
|
||||
xsct = get_xsct_path(config.vitis_path)
|
||||
if xsct:
|
||||
return _program_with_xsct(xsct, bit_path, callback, ps7_tcl)
|
||||
timeout = config.step_timeouts.get("bitstream", 60)
|
||||
return _program_with_xsct(xsct, bit_path, callback, ps7_tcl, timeout)
|
||||
|
||||
# Fallback to Vivado
|
||||
vivado = _get_vivado_path(config.vitis_path)
|
||||
@@ -191,6 +188,7 @@ def _program_with_xsct(
|
||||
bit_path: Path,
|
||||
callback: ProgressCallback = None,
|
||||
ps7_init_tcl: str = "",
|
||||
timeout: int = 60,
|
||||
) -> BitstreamResult:
|
||||
"""Program bitstream using xsct's fpga command.
|
||||
|
||||
@@ -203,6 +201,7 @@ def _program_with_xsct(
|
||||
bit_path: Path to .bit file.
|
||||
callback: Optional progress callback.
|
||||
ps7_init_tcl: Path to ps7_init.tcl for PS initialization.
|
||||
timeout: Subprocess timeout in seconds.
|
||||
|
||||
Returns:
|
||||
BitstreamResult with status.
|
||||
@@ -232,7 +231,7 @@ def _program_with_xsct(
|
||||
[xsct_path, tcl_file],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
timeout=timeout,
|
||||
)
|
||||
os.unlink(tcl_file)
|
||||
success = result.returncode == 0
|
||||
@@ -351,7 +350,8 @@ def run_elf(
|
||||
|
||||
# Auto-detect ps7_init.tcl from config or BIT file directory
|
||||
ps7_tcl = _find_ps7_init_tcl(config)
|
||||
return _run_elf_with_xsct(xsct, elf_path, callback, ps7_tcl)
|
||||
timeout = config.step_timeouts.get("elf_download", 60)
|
||||
return _run_elf_with_xsct(xsct, elf_path, callback, ps7_tcl, timeout)
|
||||
|
||||
|
||||
def _run_elf_with_xsct(
|
||||
@@ -359,6 +359,7 @@ def _run_elf_with_xsct(
|
||||
elf_path: Path,
|
||||
callback: ProgressCallback = None,
|
||||
ps7_init_tcl: str = "",
|
||||
timeout: int = 60,
|
||||
) -> BitstreamResult:
|
||||
"""Download and run ELF using xsct's dow command.
|
||||
|
||||
@@ -399,7 +400,7 @@ def _run_elf_with_xsct(
|
||||
[xsct_path, tcl_file],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
timeout=timeout,
|
||||
)
|
||||
os.unlink(tcl_file)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user