Commit Graph
37 Commits
Author SHA1 Message Date
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
Jeremy Shen 8130578f70 fix: remove grab_set() to allow main window interaction
UART Monitor window was modal (grab_set) preventing main window
from being used while the UART window is open. Remove grab_set
to make it modeless.
2026-06-10 15:04:39 +08:00
Jeremy Shen 778bee5d19 fix: import DARK_CARD + use hasattr for vitis_path
- widgets.py: add DARK_CARD to gui.styles imports
- zynq_checker.py: wrap config.vitis_path with hasattr
- bitstream_programmer.py: wrap config.vitis_path with hasattr
2026-06-10 15:00:15 +08:00
Jeremy Shen b4126a14ac fix: add missing DARK_CARD import in widgets.py 2026-06-10 14:58:48 +08:00
Jeremy Shen 7afc9051ab fix: defer grab_set() to avoid 'window not viewable' error
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.
2026-06-10 14:53:22 +08:00
Jeremy Shen d5fd816ff9 feat: UART Monitor standalone window
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
2026-06-10 14:51:13 +08:00
Jeremy Shen 93ab224d6d feat: Xilinx Kit directory selector + multi-version scan
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)
2026-06-10 14:27:47 +08:00
Jeremy Shen 699ad82834 🐛 fix: sub-step default expanded, real-time checkbox, startup freeze, color sync
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
2026-06-10 14:11:59 +08:00
Jeremy Shen 35bd6e600c Revert sub-step custom colors — use default CTkLabel white text
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.
2026-06-10 13:54:06 +08:00
Jeremy Shen e3e3763dc4 🐛 fix: sub-step readability, erase_all workaround, error state
Fix 1 — SubStepFrame readability:
- FONT_BODY (12pt) instead of FONT_SMALL dots/labels
- Number-circle colored dots (CIRCLE_PENDING/RUNNING/COMPLETE/ERROR)
- Accent-colored labels matching StepHeader (ACCENT_*)
- Percentage label next to running phase name

Fix 2 — erase_all full-chip erase:
- Creates temp zero-filled file of flash capacity (from flash_model)
- Uses -erase_only with dummy file instead of failing -erase_all flag
- Supports s25fl256s1 (32MB) which lacks density info for -erase_all
- _flash_capacity() maps model name→bytes

Fix 3 — Error state:
- set_phase('error') shows ✗ red on all sub-steps
- Only shows 'done' (green ●) on actual success

Fix 4 — Style consistency:
- Sub-step colors match StepHeader: CIRCLE_*/ACCENT_*/SUCCESS/DANGER
2026-06-10 13:51:41 +08:00
Jeremy Shen 95c04d94ae feat: collapsible flash sub-steps UI + erase_all checkbox
Rename erase_before_program → erase_all (semantics fix):
- erase_all: true  → -erase_all -erase_only (entire flash chip)
- erase_all: false → -erase_only (sectors matching BIN size)
Both options always erase; the checkbox controls scope, not yes/no.

UI additions:
- SubStepFrame widget: collapsible Erase/Program/Verify sub-steps
  with status dots (○/◉/●) and colored labels
- Automatically expands on Step 2 start, collapses on completion
- Phase and progress driven by real-time program_flash output parsing
- Checkbox label: '整片Flash擦除 (Erase entire flash)'

subprocess_utils.py phase detection:
- Parses 'Performing Erase/Program/Verify Operation' for phase transitions
- Emits 'phase' callbacks (erase/program/verify/done)
- Progress % tracked within current phase
2026-06-10 13:45:57 +08:00
Jeremy Shen ea3e3b8ac7 feat: flash phase tracking, erase checkbox, flash_model config
Step 2 UI now shows real-time flash operation phases parsed from program_flash output:
- Erase: "Performing Erase Operation..." → sf erase → Erased: OK
- Program: "Performing Program Operation..." → write blocks 0%→100%
- Verify: "Performing Verify Operation..." → read+cmp 0%→100%

Configuration additions:
- erase_before_program checkbox: controls whether to pre-erase before programming
- flash_model: chip type for capacity reference (s25fl256s1 = 32 MiB)

subprocess_utils.py now emits 'phase' callbacks (erase/program/verify/done)
detected from program_flash output transitions.
2026-06-10 13:39:11 +08:00
Jeremy Shen 2c48224aea 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)
2026-06-10 13:21:32 +08:00
Jeremy Shen ab7264d5c7 🐛 fix(xsct): PS init before FPGA config keeps JTAG DAP alive
Root cause: fpga -file breaks the DAP unless PS is initialized first.
Solution: source ps7_init.tcl + ps7_init + targets 1 BEFORE fpga -file.

Changes:
- _program_with_xsct: init PS (clocks/DDR/MIO) before fpga -file
  → DAP stays alive, subsequent ELF download works
- _run_elf_with_xsct: simplified - no reconnect needed, DAP is healthy
  (kept ps7_init fallback for recovery)
- program_bitstream: auto-detects ps7_init.tcl from BIT file directory

Verified on hardware:
  Session 1 (BIT): connect → ps7_init → fpga → exit  (13s) ✓
  Session 2 (ELF): connect → targets 2 → dow → con    (9s)  ✓
2026-06-10 12:53:02 +08:00
Jeremy Shen 80fb704221 🐛 fix(xsct): TCL file instead of -eval, add ps7_init, remove broken Vivado ELF path
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)
2026-06-10 12:47:59 +08:00
Jeremy Shen cddacc5d9f ♻️ refactor(toolchain): replace impact/bootgen with xsct/program_flash + add fsbl
BREAKING: Replaces obsolete impact (ISE-era) and misused bootgen with
the correct Xilinx 2018.3–2023.2 toolchain.

Toolchain changes:
- vitis_checker: impact → xsct + program_flash + bootgen detection
- flash_programmer: bootgen/impact → program_flash (erase + program + verify)
- bitstream_programmer: impact → xsct fpga/dow (primary), vivado (fallback)
- Removed duplicated _get_impact_path() — now shared from vitis_checker

Config additions:
- fsbl_elf_path: FSBL ELF required by program_flash for QSPI init
- flash_type: QSPI mode (default: qspi-x4-single)

GUI additions:
- FSBL ELF file selector in Configuration panel
- Step 2 validates FSBL ELF is selected
- Step 1 now shows xsct/program_flash/bootgen in tool check
2026-06-10 12:13:02 +08:00
Jeremy Shen 1ad2dcdeb9 fix(config): correct path to project root config.yaml
- _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
2026-06-10 11:50:14 +08:00
Jeremy Shen 8f83fa9eae fix(config): save user config to project root 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)
2026-06-10 11:47:43 +08:00
Jeremy Shen 1bbce6e72a 🐛 fix(config): save to config.yaml instead of default_config.yaml
- _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
2026-06-10 11:21:34 +08:00
Jeremy Shen 9de0153854 🐛 fix(gui): fix config save and UI freeze on startup
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
2026-06-10 10:58:29 +08:00
Jeremy Shen 7ac04a6bea feat(serial): add version test via 'ver()' command
- 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
2026-06-10 10:20:05 +08:00
Jeremy Shen fddcc3689a 🐛 fix(gui): make UART unavailable notification persistent
- 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
2026-06-10 10:06:50 +08:00
Jeremy Shen b222bd4586 🐛 fix(gui): allow all steps to be runnable independently
- 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
2026-06-10 10:00:28 +08:00
Jeremy Shen f7cf179aa4 🐛 fix(gui): single-step execution now runs only selected step
- Add single_step parameter to _run_steps_worker
- _execute_step now passes single_step=True
- _run_all_steps passes single_step=False explicitly
- Single step execution no longer runs subsequent steps
2026-06-10 09:53:42 +08:00
Jeremy Shen 0af388a8f6 🐛 fix(gui): add time import and fix left panel layout scaling
- 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
2026-06-10 09:46:28 +08:00
Jeremy Shen 62609b6e80 feat(jtag): enhance Zynq JTAG detection with PS/PL status
- 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
2026-06-10 09:43:02 +08:00
Jeremy Shen 2bde6d869e feat(jtag): add Zynq JTAG presence detection via hw_server
- 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 ✓)
2026-06-09 18:33:50 +08:00
Jeremy Shen 90c3dca8b8 fix(vitis): add xsct fallback for impact detection in Xilinx 2023.2
- 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
2026-06-09 18:07:39 +08:00
Jeremy Shen 3a9bc5d9ed feat(uart): add UART availability check, inter-step delays, and post-download Zynq status verification
- Add check_uart_available() to serial_monitor.py — detects port availability
- Add verify_zynq_status() to boot_verifier.py — lightweight post-download check
- Add uart_delay / inter_step_delay config fields (defaults: 3s / 2s)
- Step 1: Check UART availability, show non-blocking 'UART 不可用' notification
- Step 4: Add post-download Zynq status check (ping + serial) after TFTP
- _run_steps_worker: Add configurable inter-step delays between all steps
- Update step labels (4a→4b→4c→4d) for clarity
2026-06-09 17:53:48 +08:00
Jeremy Shen 91a7843dbe 🐛 fix(gui): resolve step header layout overlaps and improve scaling
- 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
2026-06-09 17:30:36 +08:00
Jeremy Shen bda64d5f46 ♻️ refactor(gui): improve left panel readability with better fonts, colors, and layout
- 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
2026-06-09 17:12:41 +08:00
Jeremy Shen 5277c88e3d refactor(gui,config): rename file path fields and split BIN selector
- 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
2026-06-09 16:58:45 +08:00
Jeremy Shen bd39023ba7 feat: initial Zynq XC7Z100 Flasher GUI project
Add cross-platform GUI application for programming Zynq devices
with step-by-step workflow:

- CustomTkinter GUI with dark/light theme support
- 4-step workflow: Check Env → Flash → Bootloader → Main Program
- YAML configuration with relative path resolution
- Serial monitoring and boot log parsing
- TFTP upload with CRC32 verification
- SSH/serial reboot management
- Vivado/Impact tool auto-detection

Project structure:
- app.py: Entry point
- src/: Core modules (config, flash, bitstream, tftp, serial, gui)
- config/: Default configuration template
- .flasher_env/: Python virtual environment
2026-06-08 11:33:50 +08:00