Tcl automation is one of the most powerful features integrated into the Vivado and Xilinx SDK tools and should be fully exploited to maximize your productivity as an FPGA developer. In this post I’ve put together a “cheat sheet” of some of the most useful commands and tricks that you can use to get more done through Tcl scripting. If you want more things added to the list, please let me know in the comments section at the end.

Vivado Tcl Automation Cheat Sheet

Get the Vivado install path
Useful when you need access to the IP sources.

# Vivado install path (eg. "C:/Xilinx/Vivado/2016.3")
set vivado_dir $::env(XILINX_Vivado)

Get the top level module name of a Vivado project
We often need to know the top level module of a Vivado design so that we can appropriately name other things, such as the SDK hardware project. I think the easiest way to get this name is by searching for one of the files in the Vivado project that uses the top level module name. Some of these files are: *.bit, *.hwdef, *.sysdef, *.hdf

set hdf_filename [lindex [glob -dir $vivado_folder/$vivado_folder.sdk *.hdf] 0]
set hdf_filename_only [lindex [split $hdf_filename /] end]
set top_module_name [lindex [split $hdf_filename_only .] 0]

Open a Vivado project
We first open a project in the Tcl script to be able to synthesize, implement and export the design.

# Open project
open_project $origin_dir/$proj_name/$proj_name.xpr

Synthesize a Vivado project
The project has to be opened first.

# Synthesize project
launch_runs synth_1
wait_on_run synth_1

Implement a Vivado project
The project has to be opened first.

# Implement project
launch_runs impl_1 -to_step write_bitstream
wait_on_run impl_1

Export a Vivado project for SDK
The code below works out the top module name from the bitstream file, then creates a .sdk subdirectory and then uses the write_sysdef command to export the design.

# Export project to SDK
set bit_filename [lindex [glob -dir "$origin_dir/$proj_name/${proj_name}.runs/impl_1" *.bit] 0]
set bit_filename_only [lindex [split $bit_filename /] end]
set top_module_name [lindex [split $bit_filename_only .] 0]
set export_dir "$origin_dir/$proj_name/$proj_name.sdk"
file mkdir $export_dir
write_sysdef -force \
  -hwdef "$origin_dir/$proj_name/${proj_name}.runs/impl_1/$top_module_name.hwdef" \
  -bitfile "$origin_dir/$proj_name/${proj_name}.runs/impl_1/$top_module_name.bit" \
  -meminfo "$origin_dir/$proj_name/${proj_name}.runs/impl_1/$top_module_name.mmi" \

More info on Vivado Tcl
For more information on the Vivado Tcl commands, refer to the Vivado Design Suite Tcl Command Reference Guide (UG835).

Xilinx SDK Tcl Automation Cheat Sheet

Get the Xilinx SDK install path
Useful when you need access to the driver and library sources.

# SDK install path (eg. "C:/Xilinx/Vivado/2016.3")
set sdk_dir $::env(XILINX_SDK)

Create a hardware project in an SDK workspace
To create a hardware project, you need to provide a name for the hardware project, such as ${top_module_name}_hw_platform_0, and the path to the .hdf file which is the exported Vivado project and is usually located in the Vivado project files in the $vivado_folder.sdk subdirectory. You can avoid an error message, by checking to see if it exists already.

if {[file exists "$hw_project_name"] == 0} {
  createhw -name ${hw_project_name} -hwspec $hdf_filename

Get the name of the processor in the design
When creating software applications, we have to specify which processor the application will execute on. To find out what processors are available in the hardware project, you can parse the output of the getperipherals procedure. The following function will return the first processor that it finds.

# Below is an example of the output of "getperipherals":
# ================================================================================
#               IP INSTANCE   VERSION                   TYPE           IP TYPE
# ================================================================================
#            axi_ethernet_0       7.0           axi_ethernet        PERIPHERAL
#       axi_ethernet_0_fifo       4.1          axi_fifo_mm_s        PERIPHERAL
#           gmii_to_rgmii_0       4.0          gmii_to_rgmii        PERIPHERAL
#      processing_system7_0       5.5     processing_system7
#          ps7_0_axi_periph       2.1       axi_interconnect               BUS
#              ref_clk_fsel       1.1             xlconstant        PERIPHERAL
#                ref_clk_oe       1.1             xlconstant        PERIPHERAL
#                 ps7_pmu_0    1.00.a                ps7_pmu        PERIPHERAL
#                ps7_qspi_0    1.00.a               ps7_qspi        PERIPHERAL
#         ps7_qspi_linear_0    1.00.a        ps7_qspi_linear      MEMORY_CNTLR
#    ps7_axi_interconnect_0    1.00.a   ps7_axi_interconnect               BUS
#            ps7_cortexa9_0       5.2           ps7_cortexa9         PROCESSOR
#            ps7_cortexa9_1       5.2           ps7_cortexa9         PROCESSOR
#                 ps7_ddr_0    1.00.a                ps7_ddr      MEMORY_CNTLR
#            ps7_ethernet_0    1.00.a           ps7_ethernet        PERIPHERAL
#            ps7_ethernet_1    1.00.a           ps7_ethernet        PERIPHERAL
#                 ps7_usb_0    1.00.a                ps7_usb        PERIPHERAL
#                  ps7_sd_0    1.00.a               ps7_sdio        PERIPHERAL
#                  ps7_sd_1    1.00.a               ps7_sdio        PERIPHERAL
proc get_processor_name {hw_project_name} {
  set periphs [getperipherals $hw_project_name]
  # For each line of the peripherals table
  foreach line [split $periphs "\n"] {
    set values [regexp -all -inline {\S+} $line]
    # If the last column is "PROCESSOR", then get the "IP INSTANCE" name (1st col)
    if {[lindex $values end] == "PROCESSOR"} {
      return [lindex $values 0]
  return ""

Create an SDK workspace
We really just target a directory as the SDK workspace, and if it’s empty, Xilinx SDK creates all of the files to describe the workspace.

set $sdk_ws_dir "./sdk"
if {[file exists $sdk_ws_dir] == 0} {
  file mkdir $sdk_ws_dir
setws $sdk_ws_dir

Add a local repository to the workspace
If your applications have to refer to some custom or modified libraries, then you will typically place them in a remote directory and add this directory as a local SDK repository
to your workspace. You have to use the “setws” command first.

set $sdk_repo "../repo"
repo -set $sdk_repo

Create a software application from one of the Xilinx SDK templates
Xilinx SDK comes with a few software application templates which are useful for doing basic hardware tests when bringing up new boards. The code below creates the lwIP Echo Server
application. Notice that the code refers to the function get_processor_name which was described above.

# Generate the lwIP echo server application
createapp -name echo_server \
  -app {lwIP Echo Server} \
  -proc [get_processor_name $hw_project_name] \
  -hwproject ${hw_project_name} \
  -os standalone

Build the BSPs and software applications

# Build all
projects -build

More info on SDK Tcl
A simple Google search brings up lots of old information about the SDK batch mode which has changed a lot since 2016.1. The best way to get more information about the Xilinx SDK Tcl commands is by going into the Xilinx SDK Help and searching with the keywords “batch mode”:
1. Open Xilinx SDK
2. Select Help->Help Contents
3. Type “batch mode” in the search field and press Enter.
4. Click on “XSCT Commands” in the search results.
5. Click on “SDK Projects” on the XSCT Commands page that opens.

There is also some limited but recent information on this answer record:

Jeff is passionate about FPGAs, SoCs and high-performance computing, and has been writing the FPGA Developer blog since 2008. As the owner of Opsero, he leads a small team of FPGA all-stars providing start-ups and tech companies with FPGA design capability that they can call on when needed.

Facebook Twitter LinkedIn