Commit Graph
82 Commits
Author SHA1 Message Date
yuysh c9eae220f0 Fix naming 2026-06-12 17:24:01 +08:00
yuysh e09f16a3aa fix: Xilinx 2018.3 tool discovery — add SDK dir, .bat PATH search, older versions 2026-06-12 17:20:49 +08:00
yuysh 47ea614ba2 fix: switch to console=False (GUI only, no terminal window) 2026-06-12 16:56:56 +08:00
yuysh 13ed0d89f8 fix: resolve config.yaml path to exe directory when frozen (PyInstaller) 2026-06-12 16:55:35 +08:00
yuysh 0d66f65ef2 feat: add PyInstaller spec + Windows build script (console=True) 2026-06-12 16:51:26 +08:00
yuysh 4c01acdf69 fix: print log messages to terminal in addition to GUI 2026-06-12 16:38:51 +08:00
yuysh c0cac5b2c5 fix: ping_ip bool unpack, skip sub-step visibility, IP 192.168.100.11 after Step 3
- ping_ip returns bool, not tuple — fixed 2 unpack errors
- TftpSubStepFrame: _skipped set prevents set_phase from overwriting Skip
- set_expanded(True) before skip marking so UI shows it immediately
- After Step 3 success, fix IP to 192.168.100.11
2026-06-12 16:35:32 +08:00
yuysh 5ed195e2d6 fix: skip marking, fixed panel ratio, dialog hints, 30s boot, ping+UDP fallback
- TftpSubStepFrame.set_step_skipped(): ◌ Skip in WARNING color
- _reload_config(): suppress callbacks to avoid duplicate log/status wipe
- _log_all_file_info(): removed clear_info(), updates in-place per-key
- Removed duplicate _log_file_info method
- main_frame grid: uniform col group for strict 7:3 ratio
- FileSelector browse title shows expected file type hint
- boot_wait_delay: default 10→30s
- Step 4 IP fallback: ping first, then UDP scan, extra boot_delay retry
2026-06-12 16:10:28 +08:00
yuysh 639f2e3ce6 feat: 5 improvements — file info, download verify, UART warning, status display, COM port reuse
- file_utils: compute_file_info() — CRC32, human-readable size, mtime
- StatusDisplay: simplified to info-only key-value panel; status messages
  removed (log-only); file info shown in status panel at startup/select
- config: added download_verify checkbox (default ON), skips Step 4.2
- UART warning: CTkTextbox with word-wrap and auto-height
- COM port: check_uart_available / test_serial_version accept optional
  UartMonitor to avoid redundant serial opens (Error 13 on Windows)
- layout: left:right 7:3 ratio, config panel stretches full width
2026-06-12 15:47:40 +08:00
yuysh 9502bba636 fix: config save broken (relative_to→relpath), startup validation, auto-create, untrack config.yaml
- _relative_path(): replace Path.relative_to() with os.path.relpath()
  (ValueError crossing filesystems silently returned absolute paths)
- _sync_config_from_ui(): always derive relative from get_path() absolute,
  never trust stale FileSelector._relative_path after Browse
- validate_paths(): check all file paths exist on startup, clear missing
- Clear UI selectors after validation to prevent re-save of stale paths
- _load_config(): auto-create blank config.yaml when missing on disk
- .gitignore: add config.yaml (user-specific, must not be tracked)
2026-06-12 15:02:09 +08:00
yuysh 9238cbc63e fix: correct indentation errors in widgets.py (TftpSubStepFrame, UartMonitorWindow) 2026-06-12 14:14:17 +08:00
yuysh f330f8c276 fix: remove 'uboot'/'u-boot' from fail keywords — too broad
Xilinx program_flash often outputs informational lines containing
'uboot' (mini u-boot image file, U-Boot version banner) which are
not errors. The 'failed' keyword already catches 'U-Boot failed'
and 'uboot failed' in actual failure scenarios.
2026-06-11 18:13:30 +08:00
yuysh 04ad61e694 fix: convert all TCL-embedded paths to forward slashes (cross-platform)
Windows backslashes in TCL strings are interpreted as escape sequences
(\f → form feed, \t → tab). This caused 'dow "Files\fsbl..."' to
fail with garbled paths — TCL couldn't find the file.

Added _tcl_path() helper: replaces \ with / in any path before
embedding in TCL. Forward slashes work on all platforms including
Windows. Applied to all 7 path sites: source, fpga -file, dow.
2026-06-11 18:11:44 +08:00
yuysh 4ca15c1190 fix: detect flash failure from output even when returncode is 0
program_flash can return exit code 0 while outputting errors like
'uboot failed' or 'cannot open'. Previously only returncode was
checked, so these silent failures caused the workflow to continue
past a failed erase/program step.

Now scans combined stdout+stderr for failure keywords
(uboot, failed, cannot, abort, invalid, unsupported, timeout)
and returns success=False. Also checks returncode != 0 as fallback.
2026-06-11 18:00:49 +08:00
yuysh 4453fc8e12 feat: validate JTAG Zynq part matches config.zynq_part (default XC7Z100)
Step 1 now compares detected part (e.g. XC7Z035) against config's
zynq_part. If mismatch: log error, return False to block subsequent
steps. Also returns False when no Zynq detected (was always True).
2026-06-11 17:38:27 +08:00
yuysh 799da5b2f3 fix: Windows common paths try .bat before .exe (Xilinx tools are .bat)
Vitis/Vivado on Windows uses .bat wrappers, not .exe. The hardcoded
fallback paths in _find_executable step 4 only looked for .exe files.
Also expanded to try both 2023.2 and 2022.2 + Vitis/Vivado combos.
2026-06-11 17:34:36 +08:00
yuysh 7e8427d522 fix: redirect Vivado .jou/.log to temp dir with -tempDir
Vivado batch mode creates vivado.jou, vivado.log, and backup files
in cwd by default. Use -tempDir to redirect all temp output to the
system temp directory, and clean up afterwards.
2026-06-11 17:26:41 +08:00
yuysh 66c04da5e0 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.
2026-06-11 17:24:31 +08:00
yuysh 954aed7c0d chore: expand .gitignore for all Vivado/Xilinx temp files
Added: vivado.jou, vivado.log, vivado_*.backup.log, vivado_*.str,
hs_err_pid*.log (Java crash), webtalk*, usage_statistics_*
2026-06-11 17:03:18 +08:00
yuysh 474e3f122b fix: use Vivado TCL for JTAG check — xsct 'targets' has broken TCL list parsing
xsct's [targets] returns unquoted TCL lists where multi-word names
(e.g. 'ARM Cortex-A9 MPCore #0') are split into individual words
by foreach, making it impossible to reconstruct device names.

Vivado TCL with open_hw_manager works reliably:
- Cold start (no hw_server): 7.1s on Linux
- hw_server already running: 6.8s on Linux
- Provides IDCODE (converted from Vivado binary to hex), device names

Smart hw_server lifecycle:
- Check port 3121 before starting — reuse if already running
- Start hw_server only when needed (poll port up to 3s)
- Don't kill hw_server that we didn't start

Output format parsed: DEVICE:arm_dap_0 IDCODE:010010111010...0111
→ arm_dap_0 idcode=4BA00477 PS detected
→ xc7z100_1 idcode=03736093 PL detected → XC7Z100

Tested on Linux 2023.2, cold + warm start, confirmed working.
2026-06-11 17:02:23 +08:00
yuysh dd35ab0846 fix: use xsct 'jtag targets' for raw JTAG chain with IDCODE
The previous TCL used 'targets' which returns XSCT target IDs (numbers
like 1, 2, APU, xc7z100) — each word was parsed as a separate device,
producing '1 (Other)', 'APU (Other)', etc.

Switch to 'jtag targets' which outputs raw JTAG chain devices:
  1  xc7z100 (idcode 03736093 irlen 4)
  2  arm_dap (idcode 4BA00477 irlen 4)

Changes:
- New _JTAG_LINE_RE pattern for parsing jtag targets output
- Zynq pattern relaxed for xsct names (xc7z100 not xc7z100_1)
- ARM DAP pattern: arm_dap (not arm_dap_N)
- JtagDevice now carries idcode field
- ZynqDevice propagates idcode + is_programmable=True
- Removed unused tempfile/Path/os imports
2026-06-11 16:45:37 +08:00
yuysh 86d65641cc chore: gitignore Vivado/JTAG temp files, delete existing ones 2026-06-11 16:37:21 +08:00
yuysh 7716171aeb fix: use xsct (not Vivado) for JTAG check + normalize COM>=10 ports on Windows
**JTAG check**: Switch from Vivado to xsct
- xsct is already proven to work (JTAG reboot succeeds)
- Much lighter than Vivado (no GUI infrastructure / open_hw_manager)
- Uses -eval inline TCL: connect; targets; disconnect; quit
- Removes dead tempfile/Path/os imports

**Windows COM port**: Add normalize_port() to serial_monitor.py
- COM ports >= 10 require NT namespace prefix (\.\COM11)
- Without it: PermissionError(13, '拒绝访问。')
- Applied at all 7 call sites: serial_monitor (5), reboot_manager, widgets
2026-06-11 16:29:32 +08:00
yuysh 91417f609e fix: start hw_server explicitly before Vivado JTAG scan
Previously the TCL script relied on Vivado's connect_hw_server to
auto-start hw_server. On Windows this either never worked (hw_server
never shows in Task Manager) or hung indefinitely during Vivado's
slow batch-mode initialisation.

Root cause: hw_server was never started.

Changes:
- vitis_checker.py: add 'hw_server' to TOOL_ALIASES + get_hw_server_path()
- zynq_checker.py: start hw_server via Popen before Vivado, kill in finally
  - 2s sleep for port binding after start
  - timeout from config.step_timeouts.check_env (default bumped to 120s)
  - clean terminate/kill sequence in finally
- config_manager.py: check_env default 30 → 120s
2026-06-11 15:57:53 +08:00
yuysh 72d0eceb8d fix: don't restart Boot Verify pulse after step 4 already completed it
Step 4's _run_step_4_load_main_program() already marks each sub-step
individually via set_step_complete/set_step_error. But the outer
_run_steps_worker() called set_phase('Boot Verify') on success, which
re-entered the 'running' phase and started a fresh pulse animation
(because _stop_pulse cleared _pulse_job).

Fix: on success, do nothing (sub-steps are already green). Only set
error on failure.
2026-06-11 15:49:10 +08:00
yuysh 1619dc194f fix: two-phase boot wait — don't stop on version when IP still pending
Previously the boot wait loop broke on the first detection of EITHER
IP or version. The Zynq firmware outputs its version string early in
the boot sequence (e.g. firmware header), while the board IP arrives
in a later burst via 'Boot=, IP=192.168.100.13, Ver=...'. The loop
would break on the version-only signal, leaving IP empty.

Changes:
1. Phase 1: log boot signal (version) but keep polling for IP
2. Phase 2: break only when IP address is actually found
3. After timeout: final check of latest_info for late-arriving IP
4. UART buffer fallback: parse all accumulated lines for IP
   (handles lines that arrived between the last poll and exit)
5. 3s pre-scan delay before UDP discover to let network stack settle
   (board's UDP server starts at the very end of boot sequence)
2026-06-11 15:41:21 +08:00
yuysh 267cae93b6 fix: use tempfile.gettempdir() instead of hardcoded /tmp/ for JTAG TCL file
/tmp/ doesn't exist on Windows — Path('/tmp/...') resolves to C:\tmp\ which
fails with 'No such file or directory'. Use tempfile.gettempdir() for
cross-platform temp directory resolution.

Also cleaned up the get_vivado_path() call (removed obsolete vitis_path kwarg).
2026-06-11 15:36:04 +08:00
yuysh 17c8f2d68b refactor: replace all config.vitis_path with config.xilinx_path across codebase
Affected files:
- flash_programmer.py (2 call sites) — get_program_flash_path
- bitstream_programmer.py (3 get_xsct_path + 1 get_vivado_path call sites)
  Also: removed duplicate _get_vivado_path() (now uses shared one from vitis_checker)
  Also: removed unused shutil import
- reboot_manager.py (1 call site) — get_xsct_path

All now use getattr(config, 'xilinx_path', '') consistently.
The only remaining 'config.vitis_path' reference is in vitis_checker.py's
check_vitis() itself (safe legacy fallback for old configs).
2026-06-11 15:33:54 +08:00
yuysh 61f0e6cd2b 🐛 fix: Step 1 JTAG check uses shared vivado path finder + build_tool_command
Root cause (2 bugs):
1. check_zynq_jtag() used config.vitis_path which no longer exists
   (config now uses xilinx_path) → always got empty path → 'Vivado not found'
2. _get_vivado_path() only checked hardcoded Linux paths, no version
   scanning, no .bat support on Windows

Fix:
- Added get_vivado_path() in vitis_checker.py (consistent with
  get_xsct_path, get_program_flash_path, etc.)
- Uses _find_executable() shared logic: scans Vivado/*/bin +
  Vitis/*/bin, picks newest version, supports .bat on Windows
- zynq_checker.py now uses get_vivado_path() + build_tool_command()
  for proper environment setup (settings64 sourcing on Linux)
- Removed obsolete _get_vivado_path() + unused shutil import
2026-06-11 15:30:43 +08:00
yuysh 6d686964e2 🐛 fix: Step 4 boot wait uses UART window monitor data, not just main monitor
Root cause: When UART window is open, self._uart_monitor is None
(the window creates its own UartMonitor). Step 4 boot wait loop
only checked self._uart_monitor.latest_info → always saw empty data
→ fell through to IP scanning even though UART had detected the IP.

Fix:
- UartMonitorWindow.get_latest_info() exposes parsed BootInfo
- Step 4 checks both: self._uart_monitor first, then UART window
- Null guard on info before accessing .ip_address
2026-06-11 15:21:36 +08:00
yuysh da94cc6cdc 🐛 fix: lambda closure crash + .bat version noise on Windows
Bug 1 — UartMonitorWindow crash on serial open failure:
- lambda: self._show_unavailable(str(e))  →  lambda e=e: ...
- Python 3 deletes 'e' after except block; default arg captures it

Bug 2 — xsct version shows 'ECHO 处于关闭状态。' on Windows:
- .bat wrappers output 'ECHO is off' / 'ECHO 处于关闭状态。' noise
- _is_batch_noise() filters known patterns (EN+ZH)
- Version regex now scans all output lines, skipping noise lines
- Path-based fallback (2023.2) catches any remaining undetectable cases
2026-06-11 15:18:08 +08:00
yuysh 70c88815cc feat: auto-discover Zynq IP after reboot (UART or UDP scan)
After Step 4 reboot, the Zynq may obtain a different IP via DHCP.

IP discovery strategy:
1. UART available → parse IP from boot output (fast, zero network traffic)
2. UART unavailable → scan 192.168.100.11–30 via UDP ver() probe
   (port = last_octet - 3, 0.5s timeout each, ~10s max for 20 IPs)

If IP changed → auto-update config.zynq_ip + UI field
2026-06-11 15:11:59 +08:00
yuysh 32b201cf8e feat: Windows .bat support + settings64 env setup for Xilinx tools
Tool discovery:
- On Windows, scan .bat files in addition to .exe
  (Xilinx Windows tools use .bat wrappers, not .exe)
- Prefer .exe over .bat when both exist (same version dir)

Environment setup:
- _find_settings_script(): walk up from bin/ to find settings64.sh/.bat
- build_tool_command(): wrap Linux tool invocation with 'source settings64.sh'
  Windows .bat wrappers handle env internally, no extra wrapping needed
- reboot_manager.py now uses build_tool_command() for proper env

Example output (Linux):
  build_tool_command('/data/xilinx/Vitis/2023.2/bin/xsct', 'script.tcl')
  → ['bash', '-c', 'source .../settings64.sh && .../xsct script.tcl']
2026-06-11 15:08:25 +08:00
yuysh 21acc16c8c feat: smart Xilinx version scanning — pick newest, log all versions
_version discovery:
- _scan_xilinx_root() collects ALL versions under Vitis/*/bin/ and Vivado/*/bin/
- _parse_version_tuple() semantic version comparison (2023.2 > 2018.3)
- Defaults to newest version, Vitis preferred over Vivado for same version
- _find_executable() search order: PATH → legacy vitis_path → scan xilinx_root → common paths

_version reporting:
- _get_tool_version(): tries -version flag first, falls back to path parsing
- _version_from_path(): extracts version from install path (e.g., .../Vitis/2023.2/bin/xsct → 2023.2)
- Step 1 logs each tool with version: ✓ xsct: /path [v2023.2]
- If multiple versions found, lists all: xsct versions found: 2023.2 (Vitis), 2018.3 (Vivado)

Cross-platform:
- Handles Windows path separators via Path()
- Expanded common Windows install paths
2026-06-11 14:56:40 +08:00
yuysh 475042c85c 🐛 fix: JTAG reboot 'Already running' error after rst -system
rst -system inherently resumes ARM execution on Zynq.
The subsequent 'con' command fails with 'Already running'
because the core is already executing after system reset.

Fix: wrap 'con' in 'catch {}' — if core is already running
the error is silently caught; if stopped, con resumes it.
xsct exits 0 (success) in both cases.
2026-06-11 14:48:45 +08:00
yuysh f6cf5de96f feat: UART boot wait, serial validation, xsct logging, layout fix
Config:
- Add boot_wait_delay (default 10s) for post-reboot wait time

Layout:
- Restore row weights for proportional scaling when maximized
- CTkScrollableFrame handles overflow when window is small

Step 3:
- Log xsct/vivado tool output (last 20 lines per result)
  for detailed diagnostics

Step 4:
- UART boot wait: monitor for IP/version via UART after reboot
- If UART unavailable, fall back to config.boot_wait_delay
- Reduces unnecessary wait time when boot is detected early

Serial:
- Real-time port validation on selection change (CTkComboBox command)
- Persistent UART unavailable warning until valid port selected
- Auto-restart UART monitor on port switch
- Graceful cross-platform handling
2026-06-11 14:44:58 +08:00
yuysh 9b07a4d5ca feat: add scrollbar via CTkScrollableFrame for window resize
- Replace main_frame CTkFrame → CTkScrollableFrame for vertical scrollbar
- Remove main_frame.grid_rowconfigure weight — content natural height
- Remove right_frame.grid_rowconfigure weight — prevents squashing
- Left column (workflow + log) retains weight=1 for dominant width
- Right column (config/serial/utility/status) keeps natural narrow width
2026-06-11 14:31:17 +08:00
yuysh 84142bb229 🎨 style: compact 2-col Utility panel, both reboot buttons red
- Reboot (UDP) now uses DANGER_COLOR like Reboot (JTAG)
- 2×2 grid layout: reboot row + version row
- Buttons stretch with sticky=ew, equal column weights
- Title spans both columns
2026-06-11 14:27:48 +08:00
yuysh f04fbe2130 feat: add TFTP download verify, Step 4 sub-steps, and fix UI layout
- Implement _tftp_get() for downloading files from Zynq via TFTP
- Add TftpSubStepFrame widget for Step 4 progress display
- Refactor Step 4 into 5 sub-steps: Upload, Download Verify, CRC Check, Reboot, Boot Verify
- Fix SubStepFrame.reset() bug that caused collapse after execution
- Fix layout overlap: Step 4 sub-step and progress bar now use separate rows
- Improve ProgressIndicator appearance with better styling and spacing
- Fix UART monitor to start regardless of _uart_available flag
- Add CRC32 verification between uploaded and downloaded files
2026-06-11 11:47:35 +08:00
Jeremy Shen 5ce544cd3c 🐛 fix: recover JTAG DAP after flash programming with system reset
- Add preemptive rst -system at Step 3 start to clean post-flash DAP state
- Replace ineffective SLCR write with targets 1 → rst -system recovery
- Exit with clear power-cycle instructions when DAP recovery fails
- Show prominent DAP error guidance in GUI for Step 3 and Run All mode

Verified: Step 1→2→3 now passes end-to-end (340s) without board power cycle.
2026-06-10 18:29:26 +08:00
Jeremy Shen 73b8225a27 feat: DAP error detection and recovery in Step 3 TCL
- TCL now checks for ARM core visibility before attempting flow
- If ARM cores are not found (DAP APB transaction error):
  * Logs WARNING with target list
  * Attempts SLCR system reset (0xF8000200)
  * Reconnects after recovery attempt
- DAP errors detected in xsct output and logged to GUI log
- Result message shows 'DAP error — board needs power cycle'
2026-06-10 17:52:14 +08:00
Jeremy Shen 0568315b83 fix: re-read config.yaml before each workflow run
_reload_config(): re-reads config.yaml and updates all UI selectors
(FSBL, BIT, ELF, BIN, FW BIN) and IP/xilinx_path fields with latest
values from file.

Called at the top of both _execute_step() and _run_all_steps(),
so changing config.yaml externally is picked up before every run.
2026-06-10 17:04:32 +08:00
Jeremy Shen 7067ce273f feat: user TCL flow + parser dedup + Python TFTP
1. bitstream_programmer — combined BIT+ELF flow (user's TCL):
   - targets -set -nocase -filter {name =~ "arm*#0"}
   - rst -processor (bypass BootROM, block Flash boot)
   - dow fsbl → con → stop (FSBL hardware init)
   - fpga -f bit (configure PL)
   - dow app → con (run bootloader)
   - Single xsct session, no ps7_init dependency

2. serial_monitor — parser fixes:
   - Boot mode parsing: 'Boot mode is JTAG' → Boot=JTAG
   - Version noise filter: rejects '3.1','update','mode','loaded'
   - Deduplication: UartMonitor only emits when (boot_mode, IP, ver) changes
   - Fixed false match: 'Boot mode' no longer captured as version

3. tftp_manager — pure Python TFTP client:
   - _tftp_put(): RFC 1350 WRQ/DATA/ACK via UDP socket
   - No external tftp command dependency
   - tftp_download(): documented as not-implemented (needs PC server)
2026-06-10 16:57:54 +08:00
Jeremy Shen 9a7a625c46 feat: boot mode parsing, FSBL-first flow, UI reorder
1. Boot mode parsing:
   - BootInfo.boot_mode field added
   - Matches 'Boot mode is JTAG', 'Boot Mode: QSPI', etc.
   - Fixed false version match: 'Boot mode is' no longer
     captures 'mode' as a version string

2. UART window cleanup:
   - Parsed boot info (mode/IP/ver) now only goes to main log
   - Raw serial lines still appear in UART window

3. Step 3 FSBL enforcement:
   - BIT step: after fpga -file, CPU is immediately halted (stop)
     to prevent Flash code from enabling MMU
   - ELF step: always dow FSBL → con → stop → dow bootloader

4. UI reorder:
   - FSBL ELF selector moved BEFORE Bootloader BIT
   - New layout: FSBL → BIT → ELF → Bootloader BIN → Flash → FW BIN
   - Fixed grid overlap (FSBL and Flash were both at row 6)
2026-06-10 16:20:12 +08:00
Jeremy Shen 47569a0e43 refactor: ELF step uses FSBL instead of ps7_init
Problem: dow app.elf to DDR (0x100000) failed with MMU section
translation fault because the CPU had MMU enabled from previously
executed Flash code.  ps7_init could reset this but user wants
FSBL-based flow.

Fix: run_elf() now downloads FSBL first (to OCM 0x0 — always
accessible), runs it to initialize clocks/DDR/MIO and force JTAG
boot mode, then downloads the app ELF to DDR.

New flow:
  connect → targets 2 → dow fsbl.elf → con → after 3s → stop
  → dow app.elf → mwr 0xF800025C 0x1 → con

Fallback: if no FSBL configured, use direct dow (legacy path).

Removed: _run_elf_with_xsct (2-attempt ps7_init recovery) replaced
by _run_elf_via_fsbl and _run_elf_direct.
2026-06-10 16:09:05 +08:00
Jeremy Shen 8b2fac931c fix: FirmWare version parsing + prevent boot-from-Flash in ELF step
Issue 1 — FirmWare version not parsed:
  Input: [Info] FirmWare:V1.2.1.2.1_11_07_pmu_log3
  Fix:  regex [Ff]irm[Ww]are:?\s*  (was [Ff]irmware\s+ — no : or camelCase)
        Plus broader version capture: [Vv]\d+\.\d+\.\d+[^\s;,\n]*

Issue 2 — ELF loaded from Flash instead of downloaded ELF:
  Root cause: Zynq devcfg.CTRL (0xF800025C) PCAP_PROG_B may trigger
  reconfiguration from Flash after fpga, causing CPU to jump to
  Flash-loaded code instead of our dow'd ELF.

  BIT step: add ps7_post_config after fpga (finalizes PL-PS interfaces)
             add mwr 0xF800025C 0x1 (disables PCAP reconfig)
  ELF step: add mwr 0xF800025C 0x1 after dow, before con
             (both attempt-1 and attempt-2 fallback)
2026-06-10 15:50:31 +08:00
Jeremy Shen 09be20791f feat: forward UART parsed boot info to main log window
UartMonitorWindow now accepts on_log callback. When boot info
(IP, version) is parsed from serial output, it:
- Appears in the UART window's own log (prefix [UART])
- Forwards to main window's log panel via on_log callback
- Unified [UART] prefix across both windows (was [BOOT] in
  UART window, [UART] Boot detected: in main log)
2026-06-10 15:40:09 +08:00
Jeremy Shen b294dfe58a fix: ELF step no longer re-runs ps7_init unnecessarily
Problem: _run_elf_with_xsct() ALWAYS appended ps7_init before dow,
even though PS was already initialized by the prior BIT download step.
Re-running ps7_init after FPGA config resets PS registers (clocks,
DDR, MIO), which breaks subsequent ELF download.

Fix: two-attempt strategy —
  1. First try WITHOUT ps7_init (DAP alive from BIT step):
     connect → targets 2 → dow → con
  2. If that fails, retry WITH ps7_init as DAP recovery:
     connect → targets 1 → source ps7_init.tcl → ps7_init → targets 2 → dow → con

Additional improvements:
- Inner _run() helper deduplicates TCL execution logic
- Better progress messages distinguish attempts
- Extended success phrase detection (adds 'Successfully downloaded')
- Nested TimeoutExpired handling per attempt
2026-06-10 15:30:11 +08:00
Jeremy Shen a47e3b78ae fix: avoid serial port conflict between workflow and UART window
Flow execution's _start_uart_monitor() now checks if a UART window
is already monitoring before starting its own UartMonitor instance.
This prevents two UartMonitor threads from fighting over the same
serial port.

Changes:
- UartMonitorWindow: add is_monitoring property
- MainWindow._uart_window: store reference to open UART window
- _start_uart_monitor(): skip if UART window already monitoring
- _on_close(): destroy UART window on main window close
2026-06-10 15:23:28 +08:00
Jeremy Shen 2695180c8f fix: auto-start UART monitoring on port open success
_on_open_success(): directly call _on_start() instead of just
enabling the button. User no longer needs to click Start —
monitoring begins automatically when the port is available.

Start/Stop buttons remain for manual control.
2026-06-10 15:21:12 +08:00