CTkToplevel grab_set() fails when called before the window is
fully realized on screen. Defer to after(50ms) so the window
has time to become viewable.
Replace 'Read Serial' button with 'Open UART' that opens a new
UART Monitor window (CTkToplevel) with:
- Title bar with status indicator (● connecting/ready/monitoring/error)
- Port info display + Start/Stop/Clear buttons
- Large scrollable log area (CTkTextbox) with monospace font
- Auto-scroll to bottom, line counter in status bar
- Boot info parsing (IP, version) shown as [BOOT] prefix
- Background serial monitoring via UartMonitor class
UART unavailable handling:
- On init, tries to open serial port in background thread
- If port can't open → shows 'UART 不可用' state with error message
- If port opens → shows 'Ready', user clicks Start to begin monitoring
Window features:
- Modal (grab_set) — focus stays on monitor window
- Clean up UartMonitor on close (WM_DELETE_WINDOW handler)
- Thread-safe UI updates via after(0, ...)
- Reuses existing UartMonitor from serial_monitor.py
Configuration panel now has a 'Xilinx Kit:' row at the top with:
- Entry field showing current path
- [Browse] button using system directory dialog (cross-platform)
- Select root directory (e.g. /data/xilinx or C:/Xilinx)
vitis_checker.py now scans xilinx_root subdirectories:
1. System PATH (shutil.which) — works on all platforms
2. Legacy vitis_path/bin
3. xilinx_root/Vitis/*/bin and Vivado/*/bin (all versions, newest first)
4. Common install paths (Windows: C:/Xilinx, Linux: /data/xilinx, /opt/xilinx)
Multi-version: scans all version directories under Vitis/ and Vivado/,
picks the newest version for each tool.
Config: xilinx_path replaces vitis_path (backward compat kept)
1. SubStepFrame defaults to expanded (▼), not collapsed
2. erase_all checkbox has command=_on_erase_toggle → immediate config update
3. _deferred_init runs _check_vitis/_refresh_ports in background thread
Fixes UI freeze during startup (subprocess.run for tool versions)
4. Sub-step colors now match StepHeader:
- pending: gray ACCENT_PENDING (#888888)
- running: blue CIRCLE_RUNNING (#0078D4) with pulse animation
- complete: green ACCENT_COMPLETE (#107C10)
- error: red ACCENT_ERROR (#D13438)
- Running sub-step pulses between light/dark blue like StepHeader
SubStepFrame now uses default CustomTkinter label colors (white
in dark mode, black in light mode) instead of manually set
CIRCLE_*/ACCENT_* colors that were unreadable on dark backgrounds.
Simplified grid layout — no nested sub-frames.
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)
Fixes found during real hardware test:
bitstream_programmer.py:
- _program_with_xsct: use TCL file instead of xsct -eval (quit→exit fix)
- _run_elf_with_xsct: add reconnect + source ps7_init.tcl after PL config,
use target ID 2 instead of name filter, TCL file instead of -eval
- _find_ps7_init_tcl: auto-detect ps7_init.tcl from BIT file directory
- run_elf: remove Vivado fallback (program_hw_cpu is xsct, not Vivado)
- Remove dead _run_elf_with_vivado (Vivado HM can't program PS CPU)
flash_programmer.py:
- wipe_flash: -erase_only without -erase_all (s25fl256s1 compat fix)
- _find_config() and _get_user_config_path() now use parent.parent.parent
to navigate from src/gui/main_window.py to project root
- User config now saves to /data/work/prj/Zynq_Flasher/config.yaml
- _find_config() now looks for config.yaml at project root first
- _get_user_config_path() returns project root config.yaml
- config/default_config.yaml remains as template (not for saving)
- _find_config() now looks for config.yaml first, then default_config.yaml
- _load_config() sets save path to config.yaml when loading from default
- _save_config() always saves to config/config.yaml
- _on_close() saves to config.yaml on window close
- Added _get_user_config_path() helper method
Config save fix:
- Add _sync_config_from_ui() to sync UI values to Config object
- Store _ip_string_var reference for IP field sync
- Call _sync_config_from_ui() before every save operation
- Fix _on_close to sync before saving
UI freeze fix:
- Defer _check_vitis() and _refresh_ports_initial() to _deferred_init()
- Use self.after(100, ...) to defer until window is displayed
- Window now appears immediately, blocking ops run in background
- Add test_serial_version() to serial_monitor.py
- Sends 'ver()' command and parses version response
- Add 'Test Version' button in serial panel
- Displays version in status and log
- Add dedicated UART warning label that doesn't get overwritten
- _show_uart_notification now uses persistent label instead of status display
- Warning message stays visible throughout workflow execution
- Remove dependency chain that prevented steps 2,3,4 from running
- All steps now have can_run=True
- Steps can be executed individually without running previous steps
- Add missing 'import time' for _run_steps_worker
- Fix left panel layout: remove rowspan=2 to prevent overflow
- Left panel now correctly occupies only lower half of window
- Remove IP check from Step 1 environment check
- Add PS (ARM DAP) and PL detection to zynq_checker
- Log detailed JTAG chain information in Step 1
- Show PS/PL detection status in GUI logs
- Display JTAG chain devices with type classification
- Add zynq_checker.py module to detect Zynq devices on JTAG chain
- Connects to hw_server via Vivado batch mode
- Parses JTAG device names to extract part number (e.g. XC7Z100)
- Integrates into Step 1 environment check in main_window.py
- Shows JTAG status in header (JTAG: XC7Z100 ✓)
- vitis_checker: shutil.which() + xsct fallback for impact detection
- flash_programmer: _get_impact_path now checks xsct as fallback
- bitstream_programmer: _get_impact_path now checks xsct as fallback
- GUI: show '(via xsct)' alias note when xsct replaces impact
- ToolStatus: add 'alias' field for tracking which executable was found
- Fix 6 overlapping widget bugs in StepHeader (title/desc, btn/icon,
run/rerun) with proper 5-column grid layout
- Merge run/rerun buttons into single _action_btn with state-driven text
- Fix DARK_CARD NameError (undefined import → STEP_CARD_BG)
- Replace fragile vertical connector lines with full-width dividers
- Add panel.grid_columnconfigure(0, weight=1) for horizontal scaling
- Unify button text across all states (▶ pending/disabled, ↻ retry)
- Tighten padding and reduce whitespace between steps
- Replace Segoe UI with Roboto for cross-platform compatibility
- Increase font sizes for better readability (9→11, 11→12, 14→15)
- Update dark theme to VS Code-like colors (#1E1E1E bg)
- Brighten accent colors and improve contrast ratios
- Fix StepHeader: number circle background, status icon positioning
- Enlarge accent bar (4→5px) and buttons (28→32px)
- Simplify status text (Completed → Done)
- Improve connector line sizing and alignment
- Rename bit_path -> bootloader_bit_path for clarity
- Rename elf_path -> bootloader_elf_path for clarity
- Split single bin_path into bootloader_bin_path (flash) and
firmware_bin_path (TFTP upload)
- Reorder Configuration panel: BIT -> ELF -> Bootloader BIN ->
Firmware BIN
- Update display names with file extensions and context labels
- Update all step references (Step 2 flash, Step 3 bootloader,
Step 4 TFTP) to use new field names