Remote programming of an AMD (fka Xilinx) Artix-7 device



Here is how you can set up a hardware server (hw_server) in AMD Vivado 2023.2, in such a way that you can move your workbench to a virtual machine in your cloud of choice - mine is GCP.

The device in question is an AMD Artix-7 A200T, but I suspect any other device supported by Vivado will work too.

This is a much more advanced setup than what is described at the AMD help site, as that setup assumes a local server and a local hardware attachment point, and does no authentication.

In contrast, this setup uses ssh tunneling to set up a secure connection between the workbench machine and the hardware host, so that it can be used between any two machines on the Internet.


I wanted to have a remote programming setup, because I wanted to have my Vivado workbench in a virtual machine on GCP, while the actual device to be programmed would be attached locally to my development server.

You may want to do that if you have large designs which you need a lot of horsepower to synthesize. By moving your workbench to the cloud, you can set up a large enough machine that synthesis lasts a reasonable amount of time.

I originally wanted to use usbip, but I could not make it work. I got pretty far, but this low level approach just didn’t seem to work well for unreliable connections.

Luckily I realized that the hardware server utility hw_server which you can find in Vivado’s bin directory can be used from a remote machine. This is the setup I used to make this a reality.


This procedure is for a noname programming device that uses the FTD FT232H device, plugged into a local port on my local Linux server running Ubuntu 22.04. Here’s the output of lsusb:

┬─[f@filmar:~/tmp/hw_server]─[11:59:34 PM]
╰─>$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:8001 Intel Corp. Integrated Hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 004: ID 8087:0a2a Intel Corp. Bluetooth wireless interface
Bus 002 Device 005: ID 5986:0366 Bison Electronics Inc. Integrated Camera
Bus 002 Device 012: ID 0403:6014 Future Technology Devices International, Ltd FT232H Single HS USB-UART/FIFO IC
Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 002: ID 0bc2:ab24 Seagate RSS LLC Backup Plus Portable Drive

The device listed at Bus 002 Device 012 is our programmer.

The first thing to do is to install the Digilent runtime on the Linux machine This does two things: it adds an entry into /etc/udev/rules.d, and adds a utility that prevents the Linux FTDI driver from activating for the programmer. You do not want this driver to get activated.

The correct activation log in /var/run/dmesg is as follows:

2024-06-19T09:57:15.043323+00:00 filmar kernel: ftdi_sio 2-1:1.0: FTDI USB Serial Device converter detected
2024-06-19T09:57:15.043339+00:00 filmar kernel: usb 2-1: Detected FT232H
2024-06-19T09:57:15.043341+00:00 filmar kernel: usb 2-1: FTDI USB Serial Device converter now attached to ttyUSB0
2024-06-19T09:57:15.056319+00:00 filmar kernel: ftdi_sio ttyUSB0: FTDI USB Serial Device converter now disconnected from ttyUSB0
2024-06-19T09:57:15.056332+00:00 filmar kernel: ftdi_sio 2-1:1.0: device disconnected

You specifically want that last line to appear. The last two lines mean that the Linux’s fdti_sio driver has attached to the USB device, but is then immediately detached. This will allow hw_server to open the device later. No matter how counter-intuitive, this disconnection means that the programmer is ready to be used.

Another thing you must use is to remove the “manufacturer” line from /etc/udev/rules.d/52-digilent-usb.rules. This allows any device, no matter the manufacturer, to be treated as a Digilent device. Here’s how the file looks like on my machine:

ATTR{idVendor}=="1443", MODE:="666"
ACTION=="add", ATTR{idVendor}=="0403", MODE:="666", RUN+="/usr/sbin/dftdrvdtch %s{busnum} %s{devnum}"

Next, you need to excavate the hw_server and libraries from your Vivado installation on your workbench, and move them to your local Linux machine. Here is the minimum list of files you need to be able to run the server locally. You can just dump them into some directory. Here is how I did it:

┬─[f@filmar:~/tmp/hw_server]─[11:59:33 PM]
╰─>$ ls -al
total 41420
drwxrwxr-x 1 f f 280 Jun 19 02:51 ./
drwxrwxr-x 1 f f 194 Jun 26 22:10 ../
-rw-rw-r-- 1 f f 2500398 Mar 19 17:17 digilent.adept.runtime_2.27.9-amd64.deb
-rwxr-xr-x 1 f f 33311200 Jun 19 01:01 hw_server*
-rwxr-xr-x 1 f f 136352 Jun 19 01:13*
-rwxr-xr-x 1 f f 18112 Jun 19 01:14*
-rwxr-xr-x 1 f f 20808 Jun 19 01:05*
-rwxr-xr-x 1 f f 12064 Jun 19 01:13*
-rwxr-xr-x 1 f f 2347192 Jun 19 01:04*
-rwxr-xr-x 1 f f 52256 Jun 19 01:03*
-rwxr-xr-x 1 f f 3995096 Jun 19 01:13*
┬─[f@filmar:~/tmp/hw_server]─[11:59:34 PM]

This will allow you to run the hw_server from this directory as follows:

/usr/bin/ --library-path $PWD \
   ./hw_server -stcp::3121 \
   -L- -l jtag -l jtag2 \
   -l events -l slave -l proxy

The startup is a bit convoluted because for some reason my installation does not honor LD_LIBRARY_PATH, so I had to pass the --library-path directly to the interpreter. The server will be started attached to port 3121 on your local machine.

Next is to open up the ssh port on your local machine, and possibly your router, so that you can start the tunnel.

From your workbench GCP you can tunnel the port 3121 on your machine to port 3122 on GCP:

ssh -L 3122::localhost:3121 \ -p 8022

I use port 8022 for ssh to avoid exposing the standard SSH port 22. This should log you into your local Linux machine from your GCP workbench, and also tunnel the port 3121 on your local Linux machine to port 3122 on your GCP workbench.

This will allow you to attach a “remote” programmer from the Vivado hardware manager, by attaching to localhost:3122. You should be able to see the JTAG chain just as if the device had been attached to the GCP workbench. I have found this setup to work quite reliably.