fix: stream Vivado output live, remove broken normalize_port

JTAG: Start hw_server explicitly (Windows needs it), then run Vivado
TCL with Popen + line-by-line stdout streaming via callback so user
can see Vivado progress in real-time (not just on failure).

COM: Remove normalize_port — pyserial >= 3.0 handles COM>=10 prefix
internally. The manual \\.\ prefix was causing PermissionError(13)
on Windows where pyserial's own handling conflicted.
This commit is contained in:
2026-06-11 17:24:31 +08:00
parent 954aed7c0d
commit 66c04da5e0
4 changed files with 154 additions and 207 deletions
+5 -26
View File
@@ -7,7 +7,6 @@ Provides UartMonitor for continuous background monitoring.
from __future__ import annotations
import platform
import re
import threading
import time
@@ -18,26 +17,6 @@ import serial
import serial.tools.list_ports
def normalize_port(port: str) -> str:
"""Normalize a serial port name for the current platform.
On Windows, COM ports numbered >= 10 require the NT namespace
prefix (\\\\.\\COM11). Without it, CreateFile fails with
'Access denied' or 'file not found'.
Args:
port: Raw port name from config or detection.
Returns:
Normalized port name suitable for serial.Serial().
"""
if platform.system() == "Windows":
match = re.match(r"^COM(\d+)$", port, re.IGNORECASE)
if match and int(match.group(1)) >= 10:
return rf"\\.\{port}"
return port
@dataclass
class BootInfo:
"""Parsed information extracted from Zynq boot output."""
@@ -190,7 +169,7 @@ def check_uart_available(
# 3. Try to open and read
try:
with serial.Serial(normalize_port(port), baudrate, timeout=timeout) as ser:
with serial.Serial(port, baudrate, timeout=timeout) as ser:
ser.reset_input_buffer()
lines: list[str] = []
while ser.in_waiting:
@@ -228,7 +207,7 @@ def read_serial_stream(
serial.SerialException: If the port cannot be opened.
"""
lines: list[str] = []
with serial.Serial(normalize_port(port), baudrate, timeout=timeout) as ser:
with serial.Serial(port, baudrate, timeout=timeout) as ser:
# Clear any pending data
ser.reset_input_buffer()
while ser.in_waiting:
@@ -264,7 +243,7 @@ def test_serial_version(
import serial
try:
with serial.Serial(normalize_port(port), baudrate, timeout=timeout) as ser:
with serial.Serial(port, baudrate, timeout=timeout) as ser:
ser.reset_input_buffer()
ser.reset_output_buffer()
@@ -405,7 +384,7 @@ class UartMonitor:
# Quick port check before starting thread
try:
test_ser = serial.Serial(normalize_port(self._port), self._baudrate, timeout=1)
test_ser = serial.Serial(self._port, self._baudrate, timeout=1)
test_ser.close()
except serial.SerialException as e:
if self.on_error:
@@ -432,7 +411,7 @@ class UartMonitor:
"""Main read loop running in background thread."""
try:
ser = serial.Serial(
normalize_port(self._port),
self._port,
self._baudrate,
timeout=self._timeout,
)