Zynq UltraScale+ MPSoC QSPI flash is PS-only, so the existing PL-based spiOverJtag approach does not seem applicable here.
I traced AMD Vitis 2025.2 program_flash and it appears to use this flow:
program_flash starts rdi_zynq_flash.
rdi_zynq_flash talks to hw_server.
- It runs the provided FSBL through the debug interface to initialize the PS/DDR/QSPI environment.
- It loads a Xilinx-provided AArch64 QSPI helper, selected by flash type/density, from:
/opt/Xilinx/2025.2/data/xicom/cfgmem/uboot/
- It sends commands/data over XSDB/XMD/hw_server to that helper, which performs QSPI erase/write/read using the PS QSPI controller and MIO pins.
The helper selection appears to be by file name, for example:
zynqmp_qspi_x8_dual_parallel_1024.bin
zynqmp_qspi_x4_single.bin
zynqmp_qspi_x1_single.bin
Those files are not plain binaries. file reports:
ELF 64-bit LSB executable, ARM aarch64, statically linked
readelf shows:
Entry point address: 0xfffc0000
Section .data address: 0xfffc0000
So it is an AArch64 U-Boot image linked to run from ZynqMP OCM at 0xfffc0000.
The shipped images look like small boardless U-Boot builds, likely from AMD/Xilinx u-boot-xlnx. Strings in the helper identify it clearly:
U-Boot 2025.01
ZynqMP MINI QSPI PARALLEL
ZynqMP>
spi@ff0f0000
xlnx,zynqmp-qspi-1.0
jedec,spi-nor
parallel-memories
spi-rx-bus-width
SPI flash sub-system
Other variants show:
- ZynqMP MINI QSPI SINGLE
- ZynqMP MINI QSPI X1 SINGLE
The Vitis host library also builds normal U-Boot commands. I decoded these command templates from:
/opt/Xilinx/2025.2/Vitis/lib/lnx64.o/libxv_xicom_hw.so
sf erase %X %X
sf read %X %X %X
sf write %X %X %X
cp.b %X %X %X
nand erase %X %X
nand read %X %X %X
nand write %X %X %X
The helper itself contains the matching SPI flash strings, including probe, erase, write, and `No SPI flash selected. Please run \sf probe``.
The helper's embedded DTB sets:
aliases {
serial0 = "/dcc";
};
chosen {
stdout-path = "serial0:115200n8";
};
dcc {
compatible = "arm,dcc";
status = "okay";
};
So the mini U-Boot console is using the ARM Debug Communications Channel (arm_dcc), not a normal UART, as expected.
On the host side, hw_server / XSDB also appears to use debug streams. In /opt/Xilinx/2025.2/Vitis/lib/lnx64.o/libxv_xicom_hw.so, XicomHWXsdbInterfaceUtils::connectHWStreams() gets TX/RX stream IDs and connects them, and XicomHWDebugInterface::sendCommand() sends command strings through XicomHWStreamsWriteCmd to the helper's RX stream.
From: /opt/Xilinx/2025.2/data/xicom/cfgmem/uboot/zynqmp_qspi_x8_dual_parallel_1024.bin
llvm-objdump shows:
fffcb2e8: d5330100 mrs x0, MDCCSR_EL0
fffcb2ec: 36f7ffe0 tbz w0, #0x1e, 0xfffcb2e8
fffcb2f0: d5330500 mrs x0, DBGDTRRX_EL0
fffcb2f4: d65f03c0 ret
fffcb310: d5330102 mrs x2, MDCCSR_EL0
fffcb314: 37efff62 tbnz w2, #0x1d, 0xfffcb300
fffcb318: d5130501 msr DBGDTRTX_EL0, x1
fffcb31c: 52800000 mov w0, #0x0
fffcb32c: d5330100 mrs x0, MDCCSR_EL0
fffcb330: 12020000 and w0, w0, #0x40000000
fffcb338: d5330100 mrs x0, MDCCSR_EL0
fffcb33c: d2630000 eor x0, x0, #0x20000000
fffcb340: 531d7400 ubfx w0, w0, #29, #1
This is exactly the ARM DCC path:
MDCCSR_EL0 checks DCC status bits
DBGDTRRX_EL0 reads debugger -> CPU data
DBGDTRTX_EL0 writes CPU -> debugger data
So the command path seems to be:
program_flash -> hw_server -> XSDB run-control streams -> target DCC-backed console -> U-Boot command loop
Question:
Would it be acceptable to bundle a copy of the Vitis-provided U-Boot images used by program_flash, or would we need to build equivalent helpers from source instead?
If source is required, could we use upstream u-boot-xlnx and compile the different images when compiling openFPGALoader?
Zynq UltraScale+ MPSoC QSPI flash is PS-only, so the existing PL-based
spiOverJtagapproach does not seem applicable here.I traced AMD Vitis 2025.2
program_flashand it appears to use this flow:program_flashstartsrdi_zynq_flash.rdi_zynq_flashtalks tohw_server./opt/Xilinx/2025.2/data/xicom/cfgmem/uboot/The helper selection appears to be by file name, for example:
zynqmp_qspi_x8_dual_parallel_1024.binzynqmp_qspi_x4_single.binzynqmp_qspi_x1_single.binThose files are not plain binaries.
filereports:readelfshows:So it is an AArch64 U-Boot image linked to run from ZynqMP OCM at
0xfffc0000.The shipped images look like small boardless U-Boot builds, likely from AMD/Xilinx u-boot-xlnx. Strings in the helper identify it clearly:
Other variants show:
The Vitis host library also builds normal U-Boot commands. I decoded these command templates from:
The helper itself contains the matching SPI flash strings, including probe, erase, write, and `No SPI flash selected. Please run \sf probe``.
The helper's embedded DTB sets:
So the mini U-Boot console is using the ARM Debug Communications Channel (
arm_dcc), not a normal UART, as expected.On the host side, hw_server / XSDB also appears to use debug streams. In
/opt/Xilinx/2025.2/Vitis/lib/lnx64.o/libxv_xicom_hw.so,XicomHWXsdbInterfaceUtils::connectHWStreams()gets TX/RX stream IDs and connects them, andXicomHWDebugInterface::sendCommand()sends command strings throughXicomHWStreamsWriteCmdto the helper's RX stream.From:
/opt/Xilinx/2025.2/data/xicom/cfgmem/uboot/zynqmp_qspi_x8_dual_parallel_1024.binllvm-objdumpshows:This is exactly the ARM DCC path:
MDCCSR_EL0checks DCC status bitsDBGDTRRX_EL0reads debugger -> CPU dataDBGDTRTX_EL0writes CPU -> debugger dataSo the command path seems to be:
program_flash->hw_server-> XSDB run-control streams -> target DCC-backed console -> U-Boot command loopQuestion:
Would it be acceptable to bundle a copy of the Vitis-provided U-Boot images used by
program_flash, or would we need to build equivalent helpers from source instead?If source is required, could we use upstream u-boot-xlnx and compile the different images when compiling openFPGALoader?