From ab7264d5c7726874a80a218dfc886f035b4de208 Mon Sep 17 00:00:00 2001 From: Jeremy Shen Date: Wed, 10 Jun 2026 12:53:02 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(xsct):=20PS=20init=20before?= =?UTF-8?q?=20FPGA=20config=20keeps=20JTAG=20DAP=20alive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) ✓ --- src/bitstream_programmer.py | 51 ++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/bitstream_programmer.py b/src/bitstream_programmer.py index 3801b2e..d1038c1 100644 --- a/src/bitstream_programmer.py +++ b/src/bitstream_programmer.py @@ -144,8 +144,8 @@ def program_bitstream( ) -> BitstreamResult: """Download a .bit bitstream to the Zynq PL (FPGA fabric). - Attempts xsct first (fast, lightweight), then falls back to Vivado - batch mode. + Inits PS (ps7_init) before FPGA config to keep the JTAG DAP alive, + allowing subsequent ELF download without reconnection issues. Args: config: Application configuration. @@ -166,10 +166,13 @@ def program_bitstream( if callback: callback("start", f"Loading bitstream: {bit_path.name}...") + # Find ps7_init.tcl for PS initialization + ps7_tcl = _find_ps7_init_tcl(config) + # Try xsct first (preferred) xsct = get_xsct_path(config.vitis_path) if xsct: - return _program_with_xsct(xsct, bit_path, callback) + return _program_with_xsct(xsct, bit_path, callback, ps7_tcl) # Fallback to Vivado vivado = _get_vivado_path(config.vitis_path) @@ -187,30 +190,38 @@ def _program_with_xsct( xsct_path: str, bit_path: Path, callback: ProgressCallback = None, + ps7_init_tcl: str = "", ) -> BitstreamResult: """Program bitstream using xsct's fpga command. - xsct connects to hw_server and uses the `fpga` command to download - the bitstream to the PL fabric. + Inits the PS (clocks, DDR, MIO) before FPGA configuration to keep + the JTAG DAP alive. Without PS init, the DAP breaks after fpga, + preventing subsequent ELF download. Args: xsct_path: Path to xsct executable. bit_path: Path to .bit file. callback: Optional progress callback. + ps7_init_tcl: Path to ps7_init.tcl for PS initialization. Returns: BitstreamResult with status. """ if callback: - callback("progress", "Using xsct to download bitstream...") + callback("progress", "Initializing PS then programming FPGA...") + + # Build TCL: PS init first, then FPGA config + lines = ["connect"] + if ps7_init_tcl and Path(ps7_init_tcl).exists(): + lines.append("targets 1") + lines.append(f'source "{ps7_init_tcl}"') + lines.append("ps7_init") + if callback: + callback("progress", "PS initialized (clocks, DDR, MIO)") + lines.append(f'fpga -file "{bit_path}"') + lines.append("exit") + tcl_script = "\n".join(lines) - # xsct TCL: connect to hw_server, target FPGA, download bitstream - tcl_script = f""" -connect -targets -fpga -file "{bit_path}" -exit -""" import tempfile, os with tempfile.NamedTemporaryFile(mode='w', suffix='.tcl', delete=False) as f: f.write(tcl_script) @@ -351,27 +362,25 @@ def _run_elf_with_xsct( ) -> BitstreamResult: """Download and run ELF using xsct's dow command. - After PL configuration (via fpga), the DAP may be disrupted. - This function reconnects, sources ps7_init.tcl to initialize - the PS, then downloads the ELF to Cortex-A9 #0. + Assumes PS was already initialized (by a prior BIT download step). + Falls back to PS init if DAP is broken. Args: xsct_path: Path to xsct executable. elf_path: Path to .elf file. callback: Optional progress callback. - ps7_init_tcl: Path to ps7_init.tcl for PS initialization. - Auto-detected from BIT file directory if empty. + ps7_init_tcl: Optional ps7_init.tcl path for recovery. Returns: BitstreamResult with status. """ if callback: - callback("progress", "Using xsct to download ELF...") + callback("progress", "Downloading ELF...") - # Build TCL: reconnect (DAP may be broken after PL config), - # source ps7_init for PS init, then dow + con lines = ["connect"] + # Recovery: if DAP is broken, re-init PS if ps7_init_tcl and Path(ps7_init_tcl).exists(): + lines.append("targets 1") lines.append(f'source "{ps7_init_tcl}"') lines.append("ps7_init") lines.append("targets 2")