The release of Vitis Unified 2025.1 made XSCT obsolete, which was the main way to access the hardware over JTAG to load binaries or access registers, among other things. In this post, we’ll explore the new Python API that can be used to do the same.
Overview
The Vitis application programming interface (API) is an interactive and scriptable command-line interface to the Vitis IDE, and it’s based on Python for its scripting language. You can run Vitis API commands interactively or script them for automation. We will connect to the target over JTAG, list the targets, access a few registers and upload binaries.
Vitis interactive
We will be using the Vitis Unified interactive mode to issue these commands. The same commands could be added to a script for an automated run.
Enter interactive mode:
pcarr@storm:~$ vitis -i
****** Vitis Development Environment
****** Vitis v2025.1 (64-bit)
**** SW Build 6137779 on 2025-05-21-18:10:04
** Copyright 1986-2022 Xilinx, Inc. All Rights Reserved.
** Copyright 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved.
Welcome to Vitis Python Shell
Python CLI API documentation is available at
/opt/Xilinx/2025.1/Vitis/cli/api_docs/build/html/index.html
Journal file generates at /home/pcarr/vitis_journal.py
Do not use the file until the session is closed.
Vitis [1]:
Once in the Vitis shell, we’ll run interactive commands:
Load the xsdb module:
Vitis [1]: import xsdb
Start a session
Vitis [2]: s = xsdb.start_debug_session()
In my case, this IP is for the remote server connected to the target
Vitis [4]: s.connect(url="TCP:192.168.1.10:3121")
tcfchan#0
Out[4]: 'tcfchan#0'
List available targets over JTAG
Vitis [5]: s.jtag_targets()
1 Digilent JTAG-HS3 210299A8E65D
2 xczu49dr (idcode 147fe093 irlen 12 fpga)
3 bscan-switch (idcode 4900101 irlen 1)
4 debug-hub (idcode 4900220 irlen 1)
5 arm_dap (idcode 5ba00477 irlen 4)
List available targets
Vitis [6]: s.targets()
1 PS TAP
2 PMU
3 PL
4 PSU
5 RPU (Reset)
6 Cortex-R5 #0 (RPU Reset)
7 Cortex-R5 #1 (RPU Reset)
8 APU (L2 Cache Reset)
9 Cortex-A53 #0 (APU Reset)
10 Cortex-A53 #1 (APU Reset)
11 Cortex-A53 #2 (APU Reset)
12 Cortex-A53 #3 (APU Reset)
We will select the PSU (target #4) to read the register groups
Vitis [7]: s.target(4)
Out[7]: <xsdb._target.Target at 0x7f72ef34fc50>
Read all the register groups (trimmed list)
Vitis [8]: s.rrd()
adma_ch0 adma_ch1 adma_ch2
adma_ch3 adma_ch4 adma_ch5
adma_ch6 adma_ch7 afifm0
afifm1 afifm2 afifm3
...
coresight_a53_cti_2 coresight_a53_cti_3 coresight_a53_dbg_0
coresight_a53_dbg_1 coresight_a53_dbg_2 coresight_a53_dbg_3
coresight_a53_etm_0 coresight_a53_etm_1 coresight_a53_etm_2
crf_apb crl_apb csu
csudma csu_wdt ddrc
ddr_phy ddr_qos_ctrl ddr_xmpu0_cfg
...
ttc3 uart0 uart1
usb3_0 usb3_0_xhci usb3_1
usb3_1_xhci wdt
Let’s focus on the register group “crl_apb”
Vitis [9]: s.rrd('crl_apb')
err_ctrl: 00 ir_status: 00
ir_mask: 01 ir_enable
ir_disable crl_wprot: 00
iopll_ctrl: 00015a00 iopll_cfg: 7e4b0c82
iopll_frac_cfg: 00000000 rpll_ctrl: 00015a00
...
chkr7_clkb_cnt: 00000000 chkr7_ctrl: 0000
boot_mode_user: 00000100 boot_mode_por: 0222
reset_ctrl: 00 blockonly_rst: 00
reset_reason: 0061 rst_lpd_iou0: 000d
And read all bits in register “boot_mode_user”
Vitis [10]: s.rrd('crl_apb boot_mode_user')
boot_mode_user: 002
alt_boot_mode (Bits [15:12]): 0 use_alt (Bits [8]): 0
boot_mode (Bits [3:0]): 2
We can write to this register, to enable the alternate boot mode, set by the user instead of the boot pins:
Vitis [11]: s.rwr('crl_apb boot_mode_user', val=0x100)
And read it back for confirmation:
Vitis [12]: s.rrd('crl_apb boot_mode_user')
boot_mode_user: 100
alt_boot_mode (Bits [15:12]): 0 use_alt (Bits [8]): 1
boot_mode (Bits [3:0]): 0
We can also load a bitstream
Vitis [13]: s.fpga(file='system.bit')
100% 23.13MB 1.68MB/s 00:13TA
This will change the active target, to Legacy Debug Hub under PL
Vitis [14]: s.targets()
1 PS TAP
2 PMU
3 PL
4* Legacy Debug Hub
5 PSU
6 RPU (Reset)
7 Cortex-R5 #0 (RPU Reset)
8 Cortex-R5 #1 (RPU Reset)
9 APU (L2 Cache Reset)
10 Cortex-A53 #0 (APU Reset)
11 Cortex-A53 #1 (APU Reset)
12 Cortex-A53 #2 (APU Reset)
13 Cortex-A53 #3 (APU Reset)
Then, we will select APU #0 to load and run the FSBL on it:
Vitis [15]: s.target(10)
Out[15]: <xsdb._target.Target at 0x7f72ef351cd0>
Vitis [16]: s.targets()
1 PS TAP
2 PMU
3 PL
4 Legacy Debug Hub
5 PSU
6 RPU (Reset)
7 Cortex-R5 #0 (RPU Reset)
8 Cortex-R5 #1 (RPU Reset)
9 APU (L2 Cache Reset)
10* Cortex-A53 #0 (APU Reset)
11 Cortex-A53 #1 (APU Reset)
12 Cortex-A53 #2 (APU Reset)
13 Cortex-A53 #3 (APU Reset)
Issue a core reset:
Vitis [17]: s.rst(type='cores')
Load and run FSBL binary:
Vitis [16]: s.dow('../Petalinux/RTX16-2025.1/images/linux/zynqmp_fsbl.elf')
Downloading Program -- ../Petalinux/RTX16-2025.1/images/linux/zynqmp_fsbl.elf
section, .text: 0xfffc0000 - 0xfffce93f
section, .note.gnu.build-id: 0xfffce940 - 0xfffce963
section, .init: 0xfffce980 - 0xfffce9b3
section, .fini: 0xfffce9c0 - 0xfffce9f3
section, .rodata: 0xfffcea00 - 0xfffcedaf
<...>
Successfully downloaded ../Petalinux/RTX16-2025.1/images/linux/zynqmp_fsbl.elf
Setting PC to Program Start Address 0xfffc0000
Vitis [17]: s.con()
Info: Cortex-A53 #0 (target 10) Running
Vitis [18]: s.stop()
Info: Cortex-A53 #0 (target 10) Stopped at 0xfffcb5a4 (External Debug Request)
Conclusion
In this post, we explored the new Python API in Vitis Unified, that supercedes the XSCT interface in previous versions. For more details, please refer to UG1400 Vitis Embedded.