_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
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.
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
- 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
- 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.
- 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'
_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.
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)
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.
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)
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)
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
_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.
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