Vivado でプロジェクトを作る Tcl スクリプト


# はじめに

Xilinx 社のFPGA開発環境の Vivado、いちいち GUI でマウスボタンをポチポチするのは面倒です。
Vivado は Tcl スクリプトでバッチ処理が出来るので、決まり切った仕事なら Tcl スクリプトを書いて処理させたほうが楽です。
この記事では、Vivado で新しいプロジェクトを作る Tclスクリプトを解説します。

# 環境

 - Xilinx Vivado 2015.4
 - Xilinx Vivado 2016.4
 - Xilinx Vivado 2017.1
 - Xilinx Vivado 2017.2
 - Xilinx Vivado 2018.3
 - Xilinx Vivado 2019.1
 - Xilinx Vivado 2019.2 (Vitis)

# Tclスクリプトの説明

## 1 新しい空のプロジェクトを作る

まずまっさらのプロジェクトを作ります。
変数 project_name にプロジェクト名を、変数 project_directory にプロジェクトを生成するディレクトリを設定しておきます。
この例では、project_name に "project" を、project_directory にTclスクリプトのあるディレクトリを指定しています。

````Tcl:create_project.tcl
set project_directory   [file dirname [info script]]
set project_name        "project"

cd $project_directory
create_project -force $project_name $project_directory
````

お好みで project_directory をカレントディレクトリにします。project_directory をカレントディレクトリにしておいた方が、間違いがなくて良いでしょう。

create_project のオプションに -force を付けているので、もしすでにプロジェクトが存在していた場合、すでにあったプロジェクトは削除されることに注意してください。

## 2 プロジェクトにプロパティを設定する

### 2.1 プロジェクトにボードまたはデバイスを設定する

プロジェクトにボードまたはデバイスを設定します。
変数 board_part にボードの名前を設定するか、変数 device_part にデバイス名を設定します。
この例では board_part に Xilinx社のZC706評価ボードを指定しています。

````Tcl:create_project.tcl
set board_part "xilinx.com:zc706:part0:1.2"
````

どのようなボードが指定できるかは get_board_parts コマンドで調べることが出来ます。
また、次のようにすればワイルドカードを使って最新のボード情報を指定する事が出来ます。

````Tcl:create_project.tcl
set board_part [get_board_parts -quiet -latest_file_version "*zc706*"]
````

変数 board_part が設定されている場合は、そちらを優先するようにしています。もし変数 board_part も 変数 device_part も設定されていなかった場合はエラーで終了します。

````Tcl:create_project.tcl
if       {[info exists board_part ] && [string equal $board_part  "" ] == 0} {
    set_property "board_part"     $board_part      [current_project]
} elseif {[info exists device_part] && [string equal $device_part "" ] == 0} {
    set_property "part"           $device_part     [current_project]
} else {
    puts "ERROR: Please set board_part or device_part."
    return 1
}
````

### 2.2 IP リポジトリを設定する

ユーザーが作った IP を使えるように、IPリポジトリを追加します。
変数 ip_repo_path_list に追加した IP が格納されているディレクトリをリスト形式で設定します。
この例ではプロジェクトのあるディレクトリと同じ階層にある ip ディレクトリを指定しています。

````Tcl:create_project.tcl
lappend ip_repo_path_list   [file join $project_directory "ip"]
````

変数 ip_repo_path_list が存在し、かつ要素を１以上持つリストの場合、プロジェクトに変数 ip_repo_path_list に格納されている IP リポジトリをプロジェクトに追加します。

````Tcl:create_project.tcl
if {[info exists ip_repo_path_list] && [llength $ip_repo_path_list] > 0 } {
    set_property ip_repo_paths $ip_repo_path_list [current_fileset]
    update_ip_catalog
}
````

### 2.3 デフォルトライブラリや言語などのプロパティを設定する

````Tcl:create_project.tcl
set_property "default_lib"        "xil_defaultlib" [current_project]
set_property "simulator_language" "Mixed"          [current_project]
set_property "target_language"    "VHDL"           [current_project]
````

## 3ファイルセットを生成する

Vivado では各種入力ファイル(HDLや設定ファイルなど)をファイルセットで管理しています。ここではまず空のファイルセットをプロジェクトに生成します。

### 3.1 ファイルセット sources_1 を生成する

HDLやブロックデザインなど論理合成可能な各種ソースコードを管理するためのファイルセット sources_1 を新たに生成します。

````Tcl:create_project.tcl
if {[string equal [get_filesets -quiet sources_1] ""]} {
    create_fileset -srcset sources_1
}
````

### 3.2 ファイルセット constrs_1 を生成する

タイミング設定ファイルやピンアサイン指定ファイルを管理するためのファイルセット constrs_1 を新たに生成します。

````Tcl:create_project.tcl
if {[string equal [get_filesets -quiet constrs_1] ""]} {
    create_fileset -constrset constrs_1
}
````

### 3.3 ファイルセット sim_1 を生成する

シミュレーション用のファイルを管理するためのファイルセット sim_1 を新たに生成します。ここにはシミュレーション用のテストベンチや各種設定ファイルを格納します。

````Tcl:create_project.tcl
if {[string equal [get_filesets -quiet sim_1] ""]} {
    create_fileset -simset sim_1
}
````

## 4 Design Run を生成する

Vivado では論理合成(Synthesis)と配置配線(Implementation)の各工程を Design Run という単位で管理しています。この Design Run 毎に各種設定および実行します。

### 4.1 Synthesis 用 Design Run synth_1 を生成する

論理合成(Synthesis)用の Design Run synth_1 を生成します。ここで論理合成時のオプションを指定します。どのようなオプションを指定できるかはマニュアルなどを参照してください。-constrset オプションで、3.2 で生成したファイルセット constrs_1 と関連づけています。

````Tcl:create_project.tcl
set synth_1_flow     "Vivado Synthesis 2015"
set synth_1_strategy "Vivado Synthesis Defaults"
if {[string equal [get_runs -quiet synth_1] ""]} {
    create_run -name synth_1 -flow $synth_1_flow -strategy $synth_1_strategy -constrset constrs_1
} else {
    set_property flow     $synth_1_flow     [get_runs synth_1]
    set_property strategy $synth_1_strategy [get_runs synth_1]
}
current_run -synthesis [get_runs synth_1]
````

### 4.2 Implementation 用 Design Run impl_1 を生成する

配置配線やビットストリーム生成用の Design Run impl_1 を生成します。-parent_run オプションで論理合成(Synthesis)用の Design Run と関連づけています。-constrset オプションで、3.2 で生成したファイルセット constrs_1 と関連づけています。

````Tcl:create_project.tcl
set impl_1_flow      "Vivado Implementation 2015"
set impl_1_strategy  "Vivado Implementation Defaults"
if {[string equal [get_runs -quiet impl_1] ""]} {
    create_run -name impl_1 -flow $impl_1_flow -strategy $impl_1_strategy -constrset constrs_1 -parent_run synth_1
} else {
    set_property flow     $impl_1_flow      [get_runs impl_1]
    set_property strategy $impl_1_strategy  [get_runs impl_1]
}
current_run -implementation [get_runs impl_1]
````

## 5 各種ファイルを追加する

ファイルセットに各種ファイルを追加します。

###5.1 constrs_1 に設定ファイルを追加する

この例では変数 design_timing_xdc_file にファイル名が設定されていた場合、ファイルセット constrs_1 にそのファイルを追加します。

````Tcl:create_project.tcl
if {[info exists design_timing_xdc_file]} {
    add_files    -fileset constrs_1 -norecurse $design_timing_xdc_file
}
````

### 5.2 sources_1 に Block Design を追加する

この例では、変数 design_bd_tcl_file にファイル名が設定されていた場合、ファイルセット sources_1 に Block Design を追加して、さらにラッパーファイルも生成します。

````Tcl:create_project.tcl
if {[info exists design_bd_tcl_file]} {
    source $design_bd_tcl_file
    regenerate_bd_layout
    save_bd_design
    set design_bd_name  [get_bd_designs]
    make_wrapper -files [get_files $design_bd_name.bd] -top -import
}
````

ここで design_bd_tcl_file はTclスクリプトになっています。Vivado には一度 GUI で作成した Block Design を Tclスクリプトファイルに出力する機能があります。具体的には次のようにします。

````
Vivado > Flow Navigator > Open Block Design
Vivado > File > Export > Export Block Design
````

ここで出力したTclスクリプトを変数 design_bd_tcl_file に指定することが出来ます。

### 5.3 sources_1 にVHDLファイルを追加する。

VHDLファイルをファイルセット sources_1 に追加します。
VHDLファイルは多数あることが多いので、まずはファイルセットに追加するためのプロシージャを定義しておきます。

````Tcl:create_project.tcl
proc add_vhdl_file {fileset_name library_name file_name} {
    set file    [file normalize $file_name]
    set fileset [get_filesets   $fileset_name] 
    add_files -norecurse -fileset $fileset $file
    set file_obj [get_files -of_objects $fileset $file]
    set_property "file_type" "VHDL"        $file_obj
    set_property "library"   $library_name $file_obj
}
````

上記プロシージャを使って、追加したいVHDLファイルを指定します。
次の例ではファイルセット sources_1 に../../src/main/vhdl/top.vhdを追加しています。ファイル名はTclスクリプトを実行するディレクトリからの相対パスで指定しています。また、WORKライブラリに属するVHDLファイルであることを指定しています。

````Tcl:create_project.tcl
add_vhdl_file sources_1 WORK ../../src/main/vhdl/top.vhd
````

この例ではVHDLファイルを追加する方法を説明していますが、同様の方法でVerilogファイルを追加することも出来るはずです。

# Tclスクリプトの実行

## Vivado のバッチモードで実行する

Tclスクリプトを Vivado のバッチモードで実行する場合は次のようにします。

````
shell% cd project
shell% vivado -mode batch -source create_project.tcl
````

## Vivado の GUIモードから実行する

Tclスクリプトを Vivado のGUIモードから動かす場合は次のようにします。

````
Vivado > Tools > Run Tcl Script... > project/create_project.tcl
````

# Tclスクリプトサンプル

[Synthesijerで作ったモジュールをMessagePack-RPCで制御する(ZYNQ論理合成編)](http://qiita.com/ikwzm/items/0254f1d49602095b5cde)で使った create_project.tcl をサンプルとして示します。

````Tcl:create_project.tcl
#
# create_project.tcl  Tcl script for creating project
#
set     project_directory   [file dirname [info script]]
set     project_name        "project"
set     board_part          [get_board_parts -quiet -latest_file_version "*zybo*"]
set     device_parts        "xc7z010clg400-1"
set     design_bd_tcl_file  [file join $project_directory "design_1_bd.tcl"  ]
set     design_pin_xdc_file [file join $project_directory "design_1_pin.xdc" ]
lappend ip_repo_path_list   [file join $project_directory ".." ".." ".." ".." ".." ".." "PTTY_AXI" "target" "xilinx" "ip"]
lappend ip_repo_path_list   [file join $project_directory ".." ".." ".." ".." ".." ".." "LED_AXI"  "target" "xilinx" "ip"]
lappend ip_repo_path_list   [file join $project_directory ".." ".." "ip"]
#
# Create project
#
create_project -force $project_name $project_directory
#
# Set project properties
#
if       {[info exists board_part ] && [string equal $board_part  "" ] == 0} {
    set_property "board_part"     $board_part      [current_project]
} elseif {[info exists device_part] && [string equal $device_part "" ] == 0} {
    set_property "part"           $device_part     [current_project]
} else {
    puts "ERROR: Please set board_part or device_part."
    return 1
}
set_property "default_lib"        "xil_defaultlib" [current_project]
set_property "simulator_language" "Mixed"          [current_project]
set_property "target_language"    "VHDL"           [current_project]
#
# Create fileset "sources_1"
#
if {[string equal [get_filesets -quiet sources_1] ""]} {
    create_fileset -srcset sources_1
}
#
# Create fileset "constrs_1"
#
if {[string equal [get_filesets -quiet constrs_1] ""]} {
    create_fileset -constrset constrs_1
}
#
# Create fileset "sim_1"
#
if {[string equal [get_filesets -quiet sim_1] ""]} {
    create_fileset -simset sim_1
}
#
# Create run "synth_1" and set property
#
set synth_1_flow     "Vivado Synthesis 2015"
set synth_1_strategy "Vivado Synthesis Defaults"
if {[string equal [get_runs -quiet synth_1] ""]} {
    create_run -name synth_1 -flow $synth_1_flow -strategy $synth_1_strategy -constrset constrs_1
} else {
    set_property flow     $synth_1_flow     [get_runs synth_1]
    set_property strategy $synth_1_strategy [get_runs synth_1]
}
current_run -synthesis [get_runs synth_1]
#
# Create run "impl_1" and set property
#
set impl_1_flow      "Vivado Implementation 2015"
set impl_1_strategy  "Vivado Implementation Defaults"
if {[string equal [get_runs -quiet impl_1] ""]} {
    create_run -name impl_1 -flow $impl_1_flow -strategy $impl_1_strategy -constrset constrs_1 -parent_run synth_1
} else {
    set_property flow     $impl_1_flow      [get_runs impl_1]
    set_property strategy $impl_1_strategy  [get_runs impl_1]
}
current_run -implementation [get_runs impl_1]
#
# Set IP Repository
#
if {[info exists ip_repo_path_list] && [llength $ip_repo_path_list] > 0 } {
    set_property ip_repo_paths $ip_repo_path_list [current_fileset]
    update_ip_catalog
}
#
# Create block design
#
if {[info exists design_bd_tcl_file]} {
    # 
    # Read block design file
    #
    source $design_bd_tcl_file
    #
    # Save block design
    #
    regenerate_bd_layout
    save_bd_design
    #
    # Generate wrapper files
    #
    set design_bd_name  [get_bd_designs]
    make_wrapper -files [get_files $design_bd_name.bd] -top -import
}
#
# Import timing files
#
if {[info exists design_timing_xdc_file]} {
    add_files    -fileset constrs_1 -norecurse $design_timing_xdc_file
}
#
# Import pin files
#
if {[info exists design_pin_xdc_file]} {
    add_files    -fileset constrs_1 -norecurse $design_pin_xdc_file
}

````

# 参考

- [「VivadoをGUIを使わずに実行するためのTclスクリプト達」@Qiita](http://qiita.com/ikwzm/items/a0120079d2f7f86a5904)