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
This commit is contained in:
Jeremy Shen
2026-06-10 15:30:11 +08:00
parent a47e3b78ae
commit b294dfe58a
+90 -50
View File
@@ -371,74 +371,114 @@ def _run_elf_with_xsct(
) -> BitstreamResult:
"""Download and run ELF using xsct's dow command.
Assumes PS was already initialized (by a prior BIT download step).
Falls back to PS init if DAP is broken.
PS was already initialized by the prior BIT download step, so the
DAP should be alive. First attempt skips ps7_init to avoid resetting
PS state. If DAP is broken, falls back to re-init PS.
Args:
xsct_path: Path to xsct executable.
elf_path: Path to .elf file.
callback: Optional progress callback.
ps7_init_tcl: Optional ps7_init.tcl path for recovery.
timeout: Subprocess timeout in seconds.
Returns:
BitstreamResult with status.
"""
if callback:
callback("progress", "Downloading ELF...")
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")
lines.append(f'dow "{elf_path}"')
lines.append("con")
lines.append("exit")
tcl_script = "\n".join(lines)
import tempfile, os
with tempfile.NamedTemporaryFile(mode='w', suffix='.tcl', delete=False) as f:
f.write(tcl_script)
tcl_file = f.name
try:
result = subprocess.run(
[xsct_path, tcl_file],
capture_output=True,
text=True,
timeout=timeout,
)
os.unlink(tcl_file)
has_ps7 = bool(ps7_init_tcl and Path(ps7_init_tcl).exists())
success = result.returncode == 0
output = (result.stdout + result.stderr).strip()
# xsct may return non-zero even if download succeeded
if any(phrase in output for phrase in ("Download successful", "Done", "done")):
success = True
if callback:
callback(
"complete" if success else "error",
"ELF " + ("executed" if success else "failed"),
def _run(tcl: str) -> tuple[bool, str]:
"""Run TCL via xsct, return (success, output)."""
with tempfile.NamedTemporaryFile(
mode="w", suffix=".tcl", delete=False
) as tf:
tf.write(tcl)
tcl_file = tf.name
try:
result = subprocess.run(
[xsct_path, tcl_file],
capture_output=True, text=True, timeout=timeout,
)
os.unlink(tcl_file)
out = (result.stdout + result.stderr).strip()
ok = result.returncode == 0 or any(
p in out for p in (
"Download successful", "Done", "done",
"Successfully downloaded",
)
)
return ok, out
except subprocess.TimeoutExpired:
os.unlink(tcl_file)
raise
return BitstreamResult(
step="elf",
success=success,
message="ELF executed successfully" if success else "ELF execution failed",
output=output[:4000],
)
# ── Attempt 1: no ps7_init (DAP alive from BIT step) ──
if callback:
callback("progress", "Downloading ELF (DAP should be alive)...")
tcl1 = "\n".join([
"connect",
"targets 2",
"dow \"%s\"" % elf_path,
"con",
"exit",
])
try:
ok1, out1 = _run(tcl1)
except subprocess.TimeoutExpired:
os.unlink(tcl_file)
return BitstreamResult(
step="elf",
success=False,
message="xsct ELF download timed out",
return BitstreamResult(step="elf", success=False,
message="ELF download timed out")
if ok1:
if callback:
callback("complete", "ELF executed")
return BitstreamResult(step="elf", success=True,
message="ELF executed successfully",
output=out1[:4000])
# ── Attempt 2: re-init PS to recover DAP ──
if not has_ps7:
if callback:
callback("error", "ELF failed — no ps7_init.tcl for recovery")
return BitstreamResult(step="elf", success=False,
message="ELF execution failed",
output=out1[:4000])
if callback:
callback(
"progress",
"DAP broken, re-initializing PS then retrying ELF...",
)
tcl2 = "\n".join([
"connect",
"targets 1",
"source \"%s\"" % ps7_init_tcl,
"ps7_init",
"targets 2",
"dow \"%s\"" % elf_path,
"con",
"exit",
])
try:
ok2, out2 = _run(tcl2)
except subprocess.TimeoutExpired:
return BitstreamResult(step="elf", success=False,
message="ELF download (recovery) timed out")
if callback:
callback("complete" if ok2 else "error",
"ELF " + ("executed (recovery)" if ok2 else "failed after recovery"))
return BitstreamResult(
step="elf",
success=ok2,
message=("ELF executed (DAP recovery)" if ok2 else "ELF execution failed"),
output=out2[:4000],
)
# ── Full Workflow ───────────────────────────────────────────────────