Flashing QSPI over JTAG on Zynq without using GUI

This post describes how to put a BOOT.BIN into QSPI over JTAG, on the console instead of with the graphical program_flash tool.

If you want to use the Xilinx SDK GUI tool, see step 9 of this post.

You still need to have the Xilinx SDK or Vitis installed for the console process. These steps have been tested on 2021.2 (currently the newest release) and are also confirmed to work on older versions at least as far back as 2018.1.

On older versions of the Xilinx toolkit, the hw_server and program_flash tools are in the SDK's bin folder:

$ cd /tools/Xilinx/SDK/2018.1/bin   # your installation may be in a different location
$ ls
xsct     xsdk     hw_server     bootgen     program_flash     ...

On newer versions, they're in Vitis's bin folder:

$ cd /tools/Xilinx/Vitis/2021.2/bin
$ ls
xsct     xsdk     hw_server     bootgen     program_flash     ...



You will need the file you want to put into the flash (usually a BOOT.BIN), as well as the FSBL ELF.

  • If you're using Petalinux: Petalinux will create the FSBL ELF in the images/linux folder, as either zynq_fsbl.elf or zynqmp_fsbl.elf.

  • For Xilinx dev boards such as the ZC702 and ZCU102, you can get the ELF from the Petalinux BSP that Xilinx provides. After extracting the BSP, the pre-built/linux/images folder will contain the ELF (again, it'll be named either zynq_fsbl.elf or zynqmp_fsbl.elf).

  • Other boards / SOMs may also include a pre-built FSBL in their BSP.

  • If you're not using Petalinux, you can build the FSBL manually (Xilinx page, Centennial post on building it through the command line, Centennial post on building it in Vitis)

The program_flash tool loads the FSBL onto the Zynq over JTAG and uses that FSBL to operate on the QSPI. You will need to set the physical boot mode pins to JTAG and reset the system. Using the CRL_APB.BOOT_MODE_USER register as a workaround will not work.

You will also need to know which type of QSPI you have.

  • On the ZCU102, the QSPI is in the dual parallel configuration. The help output of the program_flash utility contains a list of all the supported flash types - for the ZCU102 (Zynq MPSoC) we need to use "qspi_dual_parallel" as the flash type.

  • program_flash can also operate on non-Zynq FPGAs. In that scenario, you'll need to know the exact part number of the flash on your board. Run "./program_flash -partlist" to have program_flash list the names of all the supported QSPI chips.


Programming the QSPI:

First, start the Xilinx hardware server. You can do this in two ways from the SDK's bin folder:



% connect

Resulting in:

The hardware server will print, in both cases:
INFO: hw_server application started
INFO: To connect to this hw_server instance use url: TCP:

In another terminal, run the program_flash tool:

./program_flash \
 -f /path/to/your/BOOT.BIN \
 -fsbl /path/to/your/zynq[mp]_fsbl.elf \
 -flash_type qspi_dual_parallel \
 -offset 0 \
 -cable type xilinx_tcf url tcp:localhost:3121

You'll need to adjust all of these arguments to match your system's configuration.

The -offset argument (bytes) lets you place the BOOT.BIN into an arbitrary location in the QSPI.

Additionally, you can add two more arguments:

  • -blank_check will verify that the erase operation was successful.

  • -verify will read back the contents of QSPI and check that the BOOT.BIN was written correctly.

It's possible to run program_flash within xsct, so that you don't need two terminals:

% connect
% exec program_flash -f /path/to/your/BOOT.BIN ... (all other args)

One issue with this method is that the logs that program_flash writes to the console won't be visible in real-time. Instead, all the output will be printed at once when program_flash exits.

Alternatively, you could run hw_server and move it to the background with ctrl-z bg.

98 views0 comments