One of the most annoying things when working on an early design on an FPGA development kit is a lack of run-time register interfaces without a lot of effort.
While looking for an interface that would work on basically any Vivado supported Xilinx FPGA I came across the JTAG to AXI Master core supplied by Xilinx. Unfortunately it has a cumbersome interface that is intended for the user to drive from Vivado’s TCL console which is not always the most convenient. Others have been looking for a C API to interact with the hw_server directly. There seems to be someone that has had put together a C library but I was unable to get the files. I wanted something easier to use anyways so I began to look elsewhere for a solution.
Accessing JTAG2AXI from XSDB
I remembered that XSDK, XSCT and XSDB has the ability to read/write memory on the Xilinx SoCs so I thought to try the mrd
and mwr
in XSDB.
Running xsdb in a terminal.
$> xsdb
Connecting to the hw_server and JTAG cable.
xsdb% connect tcfchan#0
Searching for debug target.
xsdb% targets 1 APU 2 ARM Cortex-A9 MPCore #0 (Running) 3 ARM Cortex-A9 MPCore #1 (Running) 4 xc7z010 5 Legacy Debug Hub 6 JTAG2AXI
We see above that the JTAG2AXI core we put in our design which we have already programmed to the board shows up so we select it.
xsdb% target 6
Trying the mrd
command results in a valid read!
xsdb% mrd 0 0: 0000000A
Accessing JTAG2AXI from Python
While performing memory read and write without the TCL commands in Vivado but from XSDB is great… I wanted a way to interact with the JTAG2AXI bridge from other software. While looking for a solution I found pysct, a Python interface to XSDB and Vivado!
After installing pysct, connecting to XSDB is as easy as starting xsdb in a terminal then creating a server and connecting to it from Python.
$> xsdb xsdb% xsdbserver start -port 3010
from pysct.core import * xsct = Xsct('localhost', 3010) xsct.do("connect") xsct.do("target 6") print(xsct.do("mrd -value 0")) # xsct.do("mrd -value 0 256") performs a read burst of 256 words instead of 1.
By default the mrd
command returns data formatted for human reading with addresses and data in HEX format. This slows stuff down a lot. Using the -value
or -bin
option is recommended for higher speed.
I noticed some issues in pysct and had to modify the recv() function in the Xsct class to have a much larger buffer size, setting it to 32768 allowed AXI4 bursts of 256 to work.
Performance Testing
With the JTAG cable on the Digilent Zybo board set to 30 MHz I ran some performance tests.
Running some performance tests on an AXI4Lite variant of the core in Python results in about 9 kilobytes/s of read transfers.
If we use the AXI4 variant of the core and use mrd -value 0 256
to perform max length bursts we get about 1.2 megabytes/s of read transfers! Pretty decent!