There are many possibilities of how you might debug your FPGA design. The most straigtforward way is to use the Chipscope (ILA – Integrated Logic Analyzer), which is accessed by JTAG interface. This is very practical, but it is intended for debug only. What if you want to configure a part of your design and optionally debug it as well? Users of SoC devices such as Zynq have the option to usually quite easily prepare a bootable image via petalinux / RTOS and issue the necessary commands to the FPGA fabric through any of the AXI_M interfaces available on the processing system. 

If you do however only have an FPGA, you dont have such option. You can use the Microblaze / Nios Softcore processors and run some form of application on these for sure, but this requires additional effort and FPGA resources. The other option is to interface to the outer world via a UART / SPI interface or in advanced cases through interfaces such as PCIe. If you want to quickly build a design and do not require some intense configuration writes, then PCIe is not a good option . What comes quite handy is the JTAG to AXI Master IP.

This IP converts the JTAG traffic to AXI4/AXI4L traffic, so that you can use it as a master for your entire AXI4 infrastructure. The only thing you need is to provide a reset and a clock, thats basically all. The minimum diagram is shown below (PDF here). So if you have ever wondered how to light-up a LED on your board (My favorite task), here is the answer. After programming the device, you have to connect via JTAG and just issue AXI4/AXI4L transactions via TCL. Advanced users can of course implement a full TCL – based DMA framework :-) 

If you want, you can of course use the JTAG2AXI Master IP only and get rid of the IP-integrator (Dont forget to assign an address for your slaves in the address editor in case you use it) and build your custom AXI4/AXI4L slave IP. This would be actually the minimal setup. After we are done with programming, here follows the TCL scripting part (From vivado TCL console):

####################################
######## CONNECT TO HARDWARE #######
# Note: To find the correct device, use: get_hw_targets
# Note: "*/xilinx_tcf/Xilinx/1234-oj1A" is my traget JTAG device name for MiniZed.
open_hw
connect_hw_server -url localhost:3121
current_hw_target [get_hw_targets */xilinx_tcf/Xilinx/1234-oj1A]

####################################################################
####### OPTIONALLY CHANGE THE JTAG FREQUENCY (Default 15MHz) #######
# Note: To list all properties: list_property [get_hw_targets \ */xilinx_tcf/Xilinx/1234-oj1A]
#get_property PARAM.FREQUENCY [get_hw_targets \ */xilinx_tcf/Xilinx/1234-oj1A]
#set_property PARAM.FREQUENCY 15000000 [get_hw_targets \ */xilinx_tcf/Xilinx/1234-oj1A]
open_hw_target

#################################
####### CONNECT TO DEVICE #######
# Note: Use get_hw_devices to find your device
current_hw_device [get_hw_devices xc7z007s_1]
# (Optional): set_property PROBES.FILE {H:/MiniZed/Mini_Work/Mini_Work.runs/Default_Impl/Minized_TOP.ltx} [lindex [get_hw_devices] 0]
# (Optional): set_property PROGRAM.FILE {H:/MiniZed/Mini_Work/Mini_Work.runs/Default_Impl/Minized_TOP.bit} [lindex [get_hw_devices] 0]
refresh_hw_device -update_hw_probes false [lindex [get_hw_devices xc7z007s_1] 0]
reset_hw_axi [get_hw_axis hw_axi_1]


###########################################
####### READ JTAG TO AXI PARAMETERS #######
# This is optional to show the JTAG2AXI IP Capabilities:
get_property AXI_ADDR_WIDTH [get_hw_axis]
get_property AXI_DATA_WIDTH [get_hw_axis]
get_property AXI_ID_WIDTH [get_hw_axis]
get_property BURST_TYPE_FIXED_SUPPORTED [get_hw_axis]
get_property BURST_TYPE_INCR_SUPPORTED [get_hw_axis]
get_property BURST_TYPE_WRAP_SUPPORTED [get_hw_axis]
get_property CLASS [get_hw_axis]
get_property PROTOCOL [get_hw_axis]
get_property STATUS.BRESP [get_hw_axis]
get_property STATUS.RRESP [get_hw_axis]

#######################################################
####### CREATE AXI4L TRANSACTIONS AS NECESSARY ########
create_hw_axi_txn Led_On  [get_hw_axis hw_axi_1] -address 80080004 -data 00000003 -type write
create_hw_axi_txn Led_Off [get_hw_axis hw_axi_1] -address 80080004 -data 00000000 -type write
create_hw_axi_txn Magic_Read [get_hw_axis hw_axi_1] -address 80080000 -type read

########################
# RUN THE TRANSACTIONS #
run_hw_axi Magic_Read
run_hw_axi Led_On  
run_hw_axi Led_Off  

Note: For heavy-duty tests and debugging, it may be convenient to read results from FPGA and write them to a file for offline analysis (via Matlab for example) :
# Open File for Writing
set Fid [open WriteTest.txt w]
set Imax 20
for {set i 0} {$i < $Imax} {incr i} {
  set CMDX [ format "%08X" $i ]
  puts $Fid $CMDX
}
# Close File 
close $Fid
TIP: You may use the ‑queue option for run_hw_axi in order to improve performance of the transfer (In case the IP is configured to support queue ).I do also recommend to add the -quiet option to the same command since it forbids the creation of additional messages in Vivado, which slightly improves performance as well. The JTAG to AXI IP may also be instantiated several times, which may comes handy as no additional AXI4/AXI4L interconnect is required. You would only access those via [get_hw_axis hw_axi_1] & [get_hw_axis hw_axi_2].

Additional good source of information about tcl usage is located in the UG835.

Additional information may be found in the IP guide (PG174) or in UG908 – Vivado Design Suite “Programming and Debugging”. Dont forget to use the docnav to always find the latest version of the associated documents! By the way: I did have 2 leds in my design Red and Green. I dont like red too much, but decided to connect it as well. They are on address 0x8008004 and use the two LSBs (theas why i issue 0x00000003). I do also have a Read-Only random magic value on address 0x80080000 of “7B8177AF” to verify the function of the interface “just in case”. And yes,I do have the most cost-optimized FPGA on the market (xc7z007s – Minized) and of course, you can use JTAG even for Zynq, although this doesnt make much sense O:)