Using the Python API in Vitis Unified

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.


Comments/Questions

Running Xilinx tools on an unsupported Linux Operating System

Xilinx tools have stringent installation requirements regarding supported Linux Distributions, especially after 2023.1, where support for RedHat EL and CentOS...

Previous