🐛 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) ✓
This commit is contained in:
+30
-21
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user