refactor: ELF step uses FSBL instead of ps7_init
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.
This commit is contained in:
+120
-78
@@ -329,7 +329,9 @@ def run_elf(
|
|||||||
) -> BitstreamResult:
|
) -> BitstreamResult:
|
||||||
"""Download and run a .elf executable on the Zynq PS ARM core.
|
"""Download and run a .elf executable on the Zynq PS ARM core.
|
||||||
|
|
||||||
Uses xsct's `dow` (download) command, with Vivado as fallback.
|
First downloads FSBL (First Stage Bootloader) to OCM (0x0) to
|
||||||
|
initialize clocks, DDR, MIO and force JTAG boot mode. Then
|
||||||
|
downloads the target ELF to DDR.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config: Application configuration.
|
config: Application configuration.
|
||||||
@@ -347,10 +349,15 @@ def run_elf(
|
|||||||
message=f"ELF file not found: {elf_path}",
|
message=f"ELF file not found: {elf_path}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fsbl_path = config.fsbl_elf_path if hasattr(config, 'fsbl_elf_path') else ""
|
||||||
|
if fsbl_path and not Path(fsbl_path).exists():
|
||||||
|
if callback:
|
||||||
|
callback("warning", f"FSBL not found: {fsbl_path}, will try direct dow")
|
||||||
|
fsbl_path = ""
|
||||||
|
|
||||||
if callback:
|
if callback:
|
||||||
callback("start", f"Running ELF: {elf_path.name}...")
|
callback("start", f"Running ELF: {elf_path.name}...")
|
||||||
|
|
||||||
# Use xsct for CPU operations (Vivado HM cannot program PS CPU)
|
|
||||||
xsct = get_xsct_path(
|
xsct = get_xsct_path(
|
||||||
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
vitis_path=config.vitis_path if hasattr(config, 'vitis_path') else "",
|
||||||
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
xilinx_root=config.xilinx_path if hasattr(config, 'xilinx_path') else "",
|
||||||
@@ -362,30 +369,32 @@ def run_elf(
|
|||||||
message="xsct not found in PATH (required for CPU operations)",
|
message="xsct not found in PATH (required for CPU operations)",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Auto-detect ps7_init.tcl from config or BIT file directory
|
|
||||||
ps7_tcl = _find_ps7_init_tcl(config)
|
|
||||||
timeout = config.step_timeouts.get("elf_download", 60)
|
timeout = config.step_timeouts.get("elf_download", 60)
|
||||||
return _run_elf_with_xsct(xsct, elf_path, callback, ps7_tcl, timeout)
|
|
||||||
|
if fsbl_path:
|
||||||
|
return _run_elf_via_fsbl(xsct, elf_path, fsbl_path, callback, timeout)
|
||||||
|
else:
|
||||||
|
return _run_elf_direct(xsct, elf_path, callback, timeout)
|
||||||
|
|
||||||
|
|
||||||
def _run_elf_with_xsct(
|
def _run_elf_via_fsbl(
|
||||||
xsct_path: str,
|
xsct_path: str,
|
||||||
elf_path: Path,
|
elf_path: Path,
|
||||||
|
fsbl_path: str,
|
||||||
callback: ProgressCallback = None,
|
callback: ProgressCallback = None,
|
||||||
ps7_init_tcl: str = "",
|
|
||||||
timeout: int = 60,
|
timeout: int = 60,
|
||||||
) -> BitstreamResult:
|
) -> BitstreamResult:
|
||||||
"""Download and run ELF using xsct's dow command.
|
"""Download and run ELF via FSBL bootloader.
|
||||||
|
|
||||||
PS was already initialized by the prior BIT download step, so the
|
FSBL loads to OCM (0x0) which is always accessible even when
|
||||||
DAP should be alive. First attempt skips ps7_init to avoid resetting
|
MMU is enabled. FSBL initializes clocks/DDR/MIO and forces
|
||||||
PS state. If DAP is broken, falls back to re-init PS.
|
JTAG boot mode, making DDR accessible for the app ELF.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
xsct_path: Path to xsct executable.
|
xsct_path: Path to xsct executable.
|
||||||
elf_path: Path to .elf file.
|
elf_path: Path to target .elf file.
|
||||||
|
fsbl_path: Path to FSBL .elf file.
|
||||||
callback: Optional progress callback.
|
callback: Optional progress callback.
|
||||||
ps7_init_tcl: Optional ps7_init.tcl path for recovery.
|
|
||||||
timeout: Subprocess timeout in seconds.
|
timeout: Subprocess timeout in seconds.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -393,99 +402,132 @@ def _run_elf_with_xsct(
|
|||||||
"""
|
"""
|
||||||
import tempfile, os
|
import tempfile, os
|
||||||
|
|
||||||
has_ps7 = bool(ps7_init_tcl and Path(ps7_init_tcl).exists())
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# ── Attempt 1: no ps7_init (DAP alive from BIT step) ──
|
|
||||||
if callback:
|
if callback:
|
||||||
callback("progress", "Downloading ELF (DAP should be alive)...")
|
callback("progress", "Downloading FSBL to OCM...")
|
||||||
|
|
||||||
tcl1 = "\n".join([
|
tcl = "\n".join([
|
||||||
"connect",
|
"connect",
|
||||||
"targets 2",
|
"targets 2",
|
||||||
|
"dow \"%s\"" % fsbl_path,
|
||||||
|
"puts \"FSBL downloaded\"",
|
||||||
|
"con",
|
||||||
|
"after 3000",
|
||||||
|
"stop",
|
||||||
|
"puts \"FSBL done, CPU halted\"",
|
||||||
"dow \"%s\"" % elf_path,
|
"dow \"%s\"" % elf_path,
|
||||||
"mwr 0xF800025C 0x00000001",
|
"mwr 0xF800025C 0x00000001",
|
||||||
"con",
|
"con",
|
||||||
|
"puts \"App running\"",
|
||||||
"exit",
|
"exit",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="w", suffix=".tcl", delete=False
|
||||||
|
) as tf:
|
||||||
|
tf.write(tcl)
|
||||||
|
tcl_file = tf.name
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ok1, out1 = _run(tcl1)
|
result = subprocess.run(
|
||||||
|
[xsct_path, tcl_file],
|
||||||
|
capture_output=True, text=True, timeout=timeout,
|
||||||
|
)
|
||||||
|
os.unlink(tcl_file)
|
||||||
|
output = (result.stdout + result.stderr).strip()
|
||||||
|
|
||||||
|
success = result.returncode == 0 or any(
|
||||||
|
p in output for p in (
|
||||||
|
"App running", "Download successful",
|
||||||
|
"Successfully downloaded",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if callback:
|
||||||
|
callback(
|
||||||
|
"complete" if success else "error",
|
||||||
|
"ELF " + ("executed via FSBL" if success else "failed"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return BitstreamResult(
|
||||||
|
step="elf",
|
||||||
|
success=success,
|
||||||
|
message="ELF executed via FSBL" if success else "ELF execution failed",
|
||||||
|
output=output[:4000],
|
||||||
|
)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
|
os.unlink(tcl_file)
|
||||||
return BitstreamResult(step="elf", success=False,
|
return BitstreamResult(step="elf", success=False,
|
||||||
message="ELF download timed out")
|
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 ──
|
def _run_elf_direct(
|
||||||
if not has_ps7:
|
xsct_path: str,
|
||||||
if callback:
|
elf_path: Path,
|
||||||
callback("error", "ELF failed — no ps7_init.tcl for recovery")
|
callback: ProgressCallback = None,
|
||||||
return BitstreamResult(step="elf", success=False,
|
timeout: int = 60,
|
||||||
message="ELF execution failed",
|
) -> BitstreamResult:
|
||||||
output=out1[:4000])
|
"""Download and run ELF directly (no FSBL, assumes DAP alive).
|
||||||
|
|
||||||
|
Used when no FSBL ELF is configured. Attempts direct dow
|
||||||
|
which only works if the CPU is in a clean state with MMU off.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
xsct_path: Path to xsct executable.
|
||||||
|
elf_path: Path to .elf file.
|
||||||
|
callback: Optional progress callback.
|
||||||
|
timeout: Subprocess timeout in seconds.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
BitstreamResult with status.
|
||||||
|
"""
|
||||||
|
import tempfile, os
|
||||||
|
|
||||||
if callback:
|
if callback:
|
||||||
callback(
|
callback("progress", "Downloading ELF directly...")
|
||||||
"progress",
|
|
||||||
"DAP broken, re-initializing PS then retrying ELF...",
|
|
||||||
)
|
|
||||||
|
|
||||||
tcl2 = "\n".join([
|
tcl = "\n".join([
|
||||||
"connect",
|
"connect",
|
||||||
"targets 1",
|
|
||||||
"source \"%s\"" % ps7_init_tcl,
|
|
||||||
"ps7_init",
|
|
||||||
"targets 2",
|
"targets 2",
|
||||||
"dow \"%s\"" % elf_path,
|
"dow \"%s\"" % elf_path,
|
||||||
"mwr 0xF800025C 0x00000001",
|
"mwr 0xF800025C 0x00000001",
|
||||||
"con",
|
"con",
|
||||||
"exit",
|
"exit",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(
|
||||||
|
mode="w", suffix=".tcl", delete=False
|
||||||
|
) as tf:
|
||||||
|
tf.write(tcl)
|
||||||
|
tcl_file = tf.name
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ok2, out2 = _run(tcl2)
|
result = subprocess.run(
|
||||||
|
[xsct_path, tcl_file],
|
||||||
|
capture_output=True, text=True, timeout=timeout,
|
||||||
|
)
|
||||||
|
os.unlink(tcl_file)
|
||||||
|
output = (result.stdout + result.stderr).strip()
|
||||||
|
|
||||||
|
success = result.returncode == 0 or any(
|
||||||
|
p in output for p in (
|
||||||
|
"Download successful", "Successfully downloaded",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if callback:
|
||||||
|
callback("complete" if success else "error",
|
||||||
|
"ELF " + ("executed" if success else "failed"))
|
||||||
|
|
||||||
|
return BitstreamResult(
|
||||||
|
step="elf",
|
||||||
|
success=success,
|
||||||
|
message="ELF executed" if success else "ELF execution failed",
|
||||||
|
output=output[:4000],
|
||||||
|
)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
|
os.unlink(tcl_file)
|
||||||
return BitstreamResult(step="elf", success=False,
|
return BitstreamResult(step="elf", success=False,
|
||||||
message="ELF download (recovery) timed out")
|
message="ELF download 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 ───────────────────────────────────────────────────
|
# ── Full Workflow ───────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user