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:
Jeremy Shen
2026-06-10 13:21:32 +08:00
parent ab7264d5c7
commit 2c48224aea
8 changed files with 434 additions and 33 deletions
+10
View File
@@ -31,6 +31,14 @@ DEFAULT_CONFIG: dict[str, Any] = {
"ping_count": 3,
"uart_delay": 3,
"inter_step_delay": 2,
"step_timeouts": {
"check_env": 30,
"flash_erase": 120,
"flash_program": 600,
"bitstream": 60,
"elf_download": 60,
"tftp_reboot": 120,
},
}
@@ -58,6 +66,7 @@ class Config:
ping_count: int = 3
uart_delay: int = 3
inter_step_delay: int = 2
step_timeouts: dict[str, int] = field(default_factory=lambda: {})
# Internal: path to the config file on disk
_config_path: Path = field(default=None, init=False, repr=False)
@@ -163,6 +172,7 @@ class Config:
"ping_count": self.ping_count,
"uart_delay": self.uart_delay,
"inter_step_delay": self.inter_step_delay,
"step_timeouts": self.step_timeouts,
}
return data