Tool Command Language (Tcl)

Tcl (Tool Command Language) is the standard language used to control Vivado® programmatically. Virtually any operation performed within the graphical user interface has a corresponding Tcl command, displayed in the Vivado console.

Getting started

In Tcl, everything is a string. Procedures are called with the procedure name followed by the list of arguments, separated by white spaces:

set a 1

The command set assigns the value 1 to the variable a. The value of a can be accessed with the dollar-sign $:

set b hello$a
# The variable `b` now contains the value `hello1`.

Procedure are defined with the following syntax:

proc concat {x y} {
  return $x$y

set c [concat $a $b]
# The variable `c` contains the value `1hello1`.

The brackets notation [] tells the intepreter to substitute [concat $a $b] with the result of the command concat $a $b. On the contrary, the brace notation {} can be used to prevent substitution:

set d {concat $a $b}
# The variable `d` contains the value `concat $a $b`.

Mathematical expressions are computed with the command expr:

proc add10 {x} {
    return [expr $x + 10]

set thirty [add10 20]

Helper procedures

Helper procedures ease the creation of Vivado block design. View these procedures on GitHub in fpga/lib/utilities.tcl.

Instantiate, Configure and Interconnect IP cores

cell from Pavel Demin

cell counter {
  CE true
} {
  CLK adc_dac/adc_clk

Instantiates a binary counter from the Xilinx library with the "clock enable" option and connect its clock to the ADC clock.

The command is equivalent to:

create_bd_cell -type ip -vlnv counter
set_property CONFIG.CE true [get_bd_cells counter]
connect_bd_net [get_bd_pins counter/CLK] [get_bd_pins adc_dac/adc_clk]

Connect cell pins when IP core already exists


# Connects the output of the binary counter to the `count` pin of the status register
connect_cell counter {
  Q  [sts_pin count]

(see sts_pin procedure below).

Connect two pins

connect_pins pin_name1 pin_name2

connect_pins counter/Q [sts_pin count]

Retrieve data from the configuration file

Get a parameter defined in config.yml

set n_adc [get_parameter n_adc]

Get info about a memory mapped region

set memory_name config
set range  [get_memory_range $memory_name]       ;# 4K
set offset [get_memory_offset $memory_name]      ;# 0x60000000
set depth  [get_memory_depth $memory_name]       ;# 1024
set width  [get_memory_addr_width $memory_name]  ;# 10

Procedures that return a pin name

Theses procedures take pin names as inputs and output pin names, allowing to chain IP creation.

Get control/status pins defined in config.yml

ctl_pin name, sts_pin name

# Connect DAC to config and ADC to status
for {set i 0} {$i < $n_adc} {incr i} {
  connect_pins [ctl_pin dac$i] adc_dac/dac[expr $i+1]
  connect_pins [sts_pin adc$i] adc_dac/adc[expr $i+1]

Slice a pin

get_slice_pin pin_name from to

# Connect the 8 least significant bits of the `led` control register to the port `led_o`:
connect_bd_net [get_bd_ports led_o] [get_bd_pins [get_slice_pin [ctl_pin led] 7 0]]

Create a constant

get_constant_pin value width

# Set the first DAC to 0
connect_pins adc_dac/dac0 [get_constant_pin 0 [get_parameter dac_width]]

Concatenate a list of pins

get_concat_pin pin_list

# Equivalent to bitshift: adc1 << 2
set pin [get_concat_pin [list [get_constant_pin 0 2] adc_dac/adc1]]

Perform logical operations on pins

set and_pin [get_and_pin pin1 pin2]
set or_pin [get_or_pin pin1 pin2]
set nor_pin [get_nor_pin pin1 pin2]
set not_pin [get_not_pin pin1]

Compare two pins

set ge_pin [get_GE_pin pin1 pin2] ;# pin1 >= pin2
set gt_pin [get_GT_pin pin1 pin2] ;# pin1 > pin2
set le_pin [get_LE_pin pin1 pin2] ;# pin1 <= pin2
set lt_pin [get_LT_pin pin1 pin2] ;# pin1 < pin2
set eq_pin [get_EQ_pin pin1 pin2] ;# pin1 == pin2
set ne_pin [get_NE_pin pin1 pin2] ;# pin1 != pin2

See also

[email protected]