前回はオリジナルのボードを作るときの I/O ピンの配置と、I/O バンクの決め方について解説しました。今回は I/O の配置を Vivado に指示する方法について解説します。
ピン配置は XDC ファイルに書く
XDC ファイルの基本
ピン配置を Vivado に指示するには XDC ファイルを書きます。
XDC は Xilinx Design Constraint (ザイリンクス・デザイン・コンストレイント) の略です。Constrait というのは制約という意味で、あまり聞きなれないかもしれません。FPGA の I/O ピンの配置のように、本来はどこにでも自由にできるものを特定のピンに束縛することから、制約と言います。
XDC ファイルの例をリスト1に示します。
set_property IOSTANDARD LVCMOS15 [get_ports {led_op[*]}]
set_property IOSTANDARD LVCMOS33 [get_ports xtalclk_ip]
set_property PACKAGE_PIN C12 [get_ports xtalclk_ip]
create_clock -name xtalclk_ip -period 20.0 [get_ports xtalclk_ip]
set_property IOSTANDARD LVCMOS33 [get_ports pushsw_ip]
set_property PACKAGE_PIN D11 [get_ports pushsw_ip]
set_property PACKAGE_PIN J1 [get_ports {gpio_op[0]}]
set_property PACKAGE_PIN J2 [get_ports {gpio_op[1]}]
Vivado の前のデザインツールである「ISE」の頃は、UCF ファイル (User Constraint File) というファイルでピン配置を制約していました。UCF ファイルの例をリスト2に示します。
NET "LED_OP<0>" LOC = "AA12" | IOSTANDARD = "LVCMOS18";
NET "LED_OP<1>" LOC = "AB11" | IOSTANDARD = "LVCMOS18";
NET "LED_OP<2>" LOC = "AC11" | IOSTANDARD = "LVCMOS18";
NET "LED_OP<3>" LOC = "AA13" | IOSTANDARD = "LVCMOS18";
NET "pushsw_ip" LOC = "Y11" | IOSTANDARD = "LVCMOS18";
NET "adc1_d0a_p_ip" LOC = "b17" | IOSTANDARD = "LVDS"| DIFF_TERM = TRUE;
NET "adc1_d0a_n_ip" LOC = "a17" | IOSTANDARD = "LVDS"| DIFF_TERM = TRUE;
NET "adc1_d1a_p_ip" LOC = "c17" | IOSTANDARD = "LVDS"| DIFF_TERM = TRUE;
NET "adc1_d1a_n_ip" LOC = "c16" | IOSTANDARD = "LVDS"| DIFF_TERM = TRUE;
UCF は1行1行が設定コマンドである単純な「設定ファイル」だったのに対し、XDC は Tcl という言語で書かれた「プログラム」になっています。
Vivado では、XDC を書いて厳密にピン配置および IOSTANDARD を決めないと Generate Bitstream でエラーとなってしまいます。
ISE の頃は未設定の I/O ピンは適当な空きピンに配置された (それは危険であるが) のですが、Vivado では厳しくなってエラー扱いとなってしまいました。また、ISE には適当に配置された I/O から UCF の雛形を作る機能があったのですが、Vivado には XDC のひな形を作る機能はないようなので、XDC について理解していなければなりません。
そこでテンプレートを作ったので活用してください。テンプレートは本稿末にあります。
Spartan-7 は Vivado でしか使えないから XDC の知識が必須
Kintex-7 や Artix-7、ZYNQ は ISE でも開発できるので、いざとなれば ISE でもなんとかなりました。しかし、Spartan-7 は発売されたタイミングが ISE のサポート終了後であったため対応していません。
必ず Vivado を使って開発することになります。もはや XDC ファイルは避けて通れません。
XDC の文法
XDC はTcl (ティクル) という言語をベースに Vivado 用の拡張コマンドを追加したものです。Tcl は古いスクリプト言語なのですが、オブジェクト指向に対応していて、CAD や電子関係の設計ツールではまだまだ現役で使用されています。
基本的には、1つ1つの「I/O ピンというオブジェクト」に対して PACKAGE_PIN と IOSTANDARD というプロパティを設定していています。
ピンを設定する記述の具体例
まず初めに、実際の XDC の例を見てみましょう。
set_property PACKAGE_PIN C12 [get_ports xtalclk_ip]
set_property PACKAGE_PIN A5 [get_ports {led_op[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_op[*]}]
文法的には
コマンド [引数1 [引数2] ・・・] [オブジェクト]
という感じです。
Tcl では { } で囲った範囲は解釈されずにそのままコマンドに渡される引数となります。これは普通の言語でいう引用符の ” ”や ‘ ’のような意味です。
また、[ ] で囲った範囲は、この部分を実行して得られた結果を前のコマンドの引数として与えるという意味となります。get_ports の返す結果はリストになりますが、ワイルドカードを使わなければ特定の1つのピンを指す「要素が1個のリスト」となります。このリストに対して set_property コマンドを実行するというわけです。
上の例の1行目は、xtalclk_ip という文字列が get_ports というコマンドに渡されます。get_ports コマンドは Tcl の機能ではなく XDC のコマンドで、Vivado で処理されます。「xtalclk_ip という名前のポート」というオブジェクトが返され、そのポートの PACKAGE_PIN というプロパティに C12 という文字列をセットします。
上の例での2行目の led_op[0] は { } で囲まれています。したがって [0] はコマンドとして解釈されるのではなく、led_op[0] という文字列が get_ports に与えられます。ポート名にバスを指定する場合は [ ] がコマンドとして実行されないように { } で囲むというお約束になります。
3行目はワイルドカード [*] を使っていますが、このワイルドカードは Tcl で解釈しているのではなく、* を含む文字列のまま get_ports に渡され、Vivado の中でパターンマッチングが動いて該当するピンのリストが返されます。複数のピンを要素に持つリストに対して一括して IOSTANDARD というプロパティを設定するわけです。
ややこしいですが、そういう文法なので仕方ありません。
ピン番号と IO 規格を指定する基本の書式
難しく考えずに、以下のパターンを覚えておきましょう。
まず、バス化されていないピンに対しては、
set_property IOSTANDARD IO規格 [get_ports ピン名] set_property PACKAGE_PIN ピン番号 [get_ports ピン名]
と書きます。
バス化されているピンに対しては、get_ports の後ろに {バス名 [番号]} を書いて、
set_property IOSTANDARD IO規格 [get_ports {バス名[番号]}] set_property PACKAGE_PIN ピン番号 [get_ports {バス名[番号]}]
と書きます。
IOSTANDARD に関してはワイルドカードが使えて、
set_property IOSTANDARD IO規格 [get_ports {バス名[*]}] set_property PACKAGE_PIN ピン番号 [get_ports {バス名[番号]}]
と書くこともできます。
具体例を見ていきましょう。
set_property IOSTANDARD LVCMOS33 [get_ports {led_op[*]}]
set_property PACKAGE_PIN A5 [get_ports {led_op[0]}]
set_property PACKAGE_PIN J5 [get_ports {led_op[1]}]
set_property PACKAGE_PIN G5 [get_ports {led_op[2]}]
set_property PACKAGE_PIN B5 [get_ports {led_op[3]}]
なお、ピン番号には “” を付けません。
IOSTANDARD の書き方
IOSTANDARD に何をかけばよいかを説明します。
基本シングルエンド規格
使用可能な IOSTANDARD のうち、基本的なシングルエンドのものを以下に示します。LVCMOS や LVTTL の書き方については疑問はないと思います。HP バンクは 1.8V までなので 2.5V 以上の規格は使えません。
IOSTANDARD名 | HRバンク | HPバンク | 備考 |
LVCMOS12 | 〇 | 〇 | |
LVCMOS15 | 〇 | 〇 | |
LVCMOS18 | 〇 | 〇 | |
LVCMOS25 | 〇 | × | HPバンクはVCCIOが1.8Vまで |
LVCMOS33 | 〇 | × | HPバンクはVCCIOが1.8Vまで |
LVTTL | 〇 | × |
基本差動規格
次は基本的な差動規格の書き方です。
LVCMOS のときにはアンダーバーが無かったのに、差動信号ではアンダーバーが登場しました。ややこしいことに、LVDS ではアンダーバーが無いほうが7シリーズで登場した HP バンクでの 1.8V VCCIO で動く LVDS で、Spartan-6 のころからの 2.5V で動いていた HR バンクでの LVDS にはアンダーバーが付くようになりました。
IOSTANDARD名 | HRバンク | HPバンク | 備考 |
TMDS_33 | 〇 | × | 主にHDMI(DVI)の送受信に使う |
LVDS | × | 〇 | |
LVDS_25 | 〇 | × |
SSTL と HSTL
終端付き信号である SSTL や HSTL の場合はさらに不規則になります。
IOSTANDARD名 | HRバンク | HPバンク | 備考 |
SSTL15 | 〇 | 〇 | DDR3で使われる |
SSTL18_I | 〇 | 〇 | 送信側に終端がない |
SSTL18_II | 〇 | 〇 | 送受信の両端に終端がある |
HSTL_I | 〇 | 〇 | 1.5V規格。送信側に終端がない |
HSTL_II | 〇 | 〇 | 1.5V規格。送受信の両端に終端がある |
HSTL_II_18 | 〇 | 〇 | 1.8V規格 |
DIFF_SSTL15 | 〇 | 〇 | DDR3のDQS信号 |
DIFF_SSTL18_I | 〇 | 〇 | 送信側に終端がない |
DIFF_SSTL18_II | 〇 | 〇 | 送受信の両端に終端がある |
DIFF_HSTL_I | 〇 | 〇 | 1.5V規格 |
DIFF_HSTL_I_18 | 〇 | 〇 | 1.8V規格 |
DIFF_SSTL15_DCI | 〇 | 1.5V以下のSSTLはクラス番号は省略 | |
DIFF_HSTL_I_DCI | 〇 | デフォルトは1.5V |
SSTL や HSTL の _I や _II というのはクラスというもので、クラス I は送信側に終端抵抗がなく、クラス II は送受信の両方に終端抵抗があります。入力側バッファでは Vref と比較して高ければ H、低ければ L となります。終端抵抗は VTT に対して特性インピーダンスと同じ抵抗でプルします。
HSTL と SSTL は似ていますが、HSTL は汎用、SSTL は主に DDR SDRAM 用なので、オリジナルのバックプレーンを作りたいのであれば HSTL を使うことになるでしょう。オシロで見た感じでは SSTL は小振幅で HSTL は大振幅という感じでした。
DIFF_SSTL や DIFF_HSTL は、シングルエンドの SSTL/HSTL を2個束ねた疑似差動で、DDR メモリのクロックや DQS に使われます。
Kintex-7 以上の HP バンクで DCI を使うときは、DIFF_HSTL_II_DCI のように、IOSTANDARD の名前の後に _DCI を付けます。
命名規則は大変分かりにくいのですが、
- HSTL は電圧を指定しないデフォルトは 1.5V である
- 1.2V 以下の SSTL は HP バンクしか使えない
- 1.5V 以下の SSTL にはクラス II しかないので、クラス番号は書かない。
- 1.5V 以下でも HSTL にはクラス I、クラス II があるのでクラス番号は省略しない。
というポイントを押さえておいてください。
オプションの設定
ドライバ強度
LVCMOS と LVTTL ではドライバ強度といって出力バッファの駆動能力を mA で記述できます。書き方は以下のとおりです。
set_property DRIVE <2 4 6 8 12 16 24> [get_portsピン名] set_property DRIVE <2 4 6 8 12 16 24> [get_ports{バス名[番号]}]
なお、LVCMOS では VCCIO 電圧によって使える電流値が異なります。筆者の感覚では、この設定が効いているかどうかはわかりません。
スルーレート
出力バッファのスルーレートはデフォルトでは SLOW に設定されていますが、高速メモリなどを使用する場合には高速化する必要があります。その場合は SLEW プロパティを設定します。
set_property SLEW <SLOW | FAST> [get_portsピン名] set_property SLEW <SLOW | FAST> [get_ports{バス名[番号]}]
スルーレートと出力電流を使えば I/O が速くなるかというと、そういうものではないようです。スルーレートを速くして電流を増やしたら動いたというような設計はしないようにしてください。
終端抵抗
LVDS では DIFF_TERM が、SSTL や HSTL では IN_TERM という内蔵終端抵抗が使えます。
これらの内蔵終端抵抗を使うと、FPGA 外に終端抵抗を置くのと比べてシグナルインテグリティを向上させることができます。ただし、VCCIO が正しい電圧になっていることが前提条件です。DIFF_TERM が使えるのは HP バンクでは 1.8V、HR バンクでは 2.5V です。
IN_TERM は、VCCIO と GND の間で 2Z0の 抵抗でテブナン終端します。FPGA 内にあるこの抵抗値は厳密ではないので、UNTUNED_SPLIT_** と呼ばれています。
書き方は、
set_property DIFF_TERM TRUE [get_portsピン名] set_property IN_TERM <UNTUNED_SPLIT_40| UNTUNED_SPLIT_50 | UNTUNED_SPLIT_60> [get_portsピン名]
です。省略しましたが、バスの指定も可能です。
プルアップ・プルダウンもしくはキーパー
Xilinx FPGA の I/O には弱いプルアップ抵抗、プルダウン抵抗、そして KEEPER 回路が入っていて、XDC ファイルで指定することでそれらを有効にすることができます。
set_property PULLUP TRUE [get_portsピン名] set_property PULLDOWN TRUE [get_portsピン名] set_property KEEPER TRUE [get_portsピン名]
KEEPER 回路というのは図4のような回路で、入力がオープンになっているときに H ならば H を保ち、L ならば L を保ちます。外部から信号が与えられたときには追従して値が変わるようフィードバックは弱く設定されています。
他にも DCI の設定など I/O に関するいろいろなことはありますが、使用頻度は多くはないので割愛させていただきます。
コンフィグ用の設定
コンフィギュレーション ROM は SPI ROM が全盛ですが、XDC ファイルに何も書かずに BitStream を作ると、SPI ROM のビット幅は 1bit でクロックが 3MHz という、極めて遅いコンフィギュレーションモードになってしまいます。これだと FPGA が起動するまでに10秒くらいかかってしまいます。
最速でコンフィギュレーションを行うビットストリームを生成するには、XDC ファイルに以下のように書きます。
set_property BITSTREAM.CONFIG.CONFIGRATE 66 [current_design] set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
SPI ROM が x2 にしか対応していない場合は SPI_BUSWIDTH は2とします。bit 幅が2というのは、MOSI と MISO を使って 2bit で送るので、どんな回路でも対応しているはずです。
また、以下の設定もしておくとよいでしょう。
set_property BITSTREAM.CONFIG.UNUSEDPIN Pullup [current_design]
set_property CFGBVS <GND | VCCO> [current_design]
set_property CONFIG_VOLTAGE <1.8 | 2.5 | 3.3> [current_design]
CONFIG_VOLTAGE は Bank0 の VCCIO 電圧です。1.8V ならば CFGBVS は GND、2.5V と 3.3V の場合は VCCO と指定します。ただし、これは指定しなくても動きますし、指定が間違っていても動くので効果のほどはわかりません。
[current_design] というのは、current_design というコマンドを実行して現在のデザインを示すオブジェクトを得ます。このオブジェクトに対して BITSTREAM.CONFIG.** というプロパティを設定するものです。current_design は、XDC でのコマンドであって、Tcl 本来の機能ではありません。
クロックの設定
クロック周波数
Vivado ではクロックの周波数は IP インテグレータ (BlockDesign) の GUI 画面で指定したり、IP の Clocking Wizard で指定するので、XDC に書くことは必須ではありません。
それでも外部から入力するクロックの周波数を指定したいのであれば、クロックの周期を用いて、
create_clock -name クロック名 -period 周期 [get_ports ピン名]
のように書きます。周期の単位は ns です。
具体例は、
create_clock -name xtalclk_ip -period 20.0 [get_ports xtalclk_ip]
です。
CLOCK_DEDICATED_ROUTE の設定
FPGA 内のクロックは MRCC から入力された信号や、MMCM の出力を BUFG につないで使うものです。しかし、不幸にも一般の I/O やロジックの出力をクロックとして使わなければならない場合もあります。
そういう場合は以下の記述を行います。
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets ネット名]
指定されたネットに対して、クロック専用配線の使用を強制しないという意味です。
ネット名はユーザがピン名に付けたものとは通常は異なり、論理合成の結果決まる文字列であるため、どのような名前になるのか予測するのは困難です。CLOCK_DEDICATED_ROUTE の設定が必要になる場合は、Vivado が出すエラーメッセージを良くみてみてください。ネット名と共に CLOCK_DEDICATED_ROUTE を FALSE にしろというヒントが出ているはずです。
XDC を書かなくてもよい場合
すべての XDC を書かなければいけないかというと、そうではありません。省略できる場合もあります。
IP コアを使う場合の XDC
ギガビットトランシーバや、DDR3 メモリを使うデザインでは、本来ならばクロックバッファの配置や、タイミングの細かいパラメータまで XDC で指定しなければなりません。
それは、一見してわけのわからない記述になります。
set_false_path -to [get_pins -hierarchical -filter {NAME =~ _txfsmresetdone_r/CLR}]
set_property LOC GTPE2_CHANNEL_X0Y3 [get_cells gtwizard_0_support_i/gtwizard_0_init_i/U0/gtwizard_0_i/gt3_gtwizard_0_i/gtpe2_i]
set_property LOC OLOGIC_X1Y57 [get_cells -hier -filter {NAME =~ /ddr_phy_4lanes_0.u_ddr_phy_4lanes/ddr_byte_lane_A.ddr_byte_lane_A/ddr_byte_group_io/slave_ts}]
これらの XDC は IP を生成したときに自動的に生成され、ユーザの知らないところで参照されています。
DDR3 メモリのピン配置や GTX のピン配置は、あらためてユーザ用 XDC に書く必要はありません。IP を生成したときに GUI の画面から設定しているはずです。
Vivado の IP コアを使った場合、ユーザが知らないところでピン配置も含めた XDC が作られ参照されているので、ピン配置の指定も省略できてしまう場合が多いです。
ZYNQ の MIO や DDR3 などの固定ピン
同様に、ZYNQ の MIO のピン配置と電圧設定と、DDR ピンなど固定された I/O ピンも XDC に書く必要はありません。が、
set_property PACKAGE_PIN V21 [get_ports FIXED_IO_ddr_vrn]
set_property PACKAGE_PIN R20 [get_ports {DDR_addr[14]}]
set_property PACKAGE_PIN K20 [get_ports {DDR_addr[1]}]
set_property PACKAGE_PIN N21 [get_ports {DDR_addr[2]}]
・・
set_property PACKAGE_PIN D26 [get_ports {FIXED_IO_mio[1]}]
set_property PACKAGE_PIN E25 [get_ports {FIXED_IO_mio[2]}]
set_property PACKAGE_PIN D25 [get_ports {FIXED_IO_mio[3]}]
set_property PACKAGE_PIN F24 [get_ports {FIXED_IO_mio[4]}]
・・・
のように書いたとしてもエラーにはなりません。
差動信号の片割れはいらない
細かいことですが、差動信号の片側は省略できます。
例えば adc_dp_ip[0]/adc_dp_in[0] という差動信号を考えます。その IOSTANDARD を LVDS にし、DIFF_TERM を有効にし、ピン配置を決定する場合、以下のようになります。
set_property IOSTANDARD LVDS [get_ports {adc_dp_ip[0]}]
set_property DIFF_TERM TRUE [get_ports {adc_dp_ip[0]}]
set_property PACKAGE_PIN B17 [get_ports {adc_dp_ip[0]}]
set_property PACKAGE_PIN A17 [get_ports {adc_dn_ip[0]}]
set_property IOSTANDARD LVDS [get_ports {adc_dn_ip[0]}]
set_property DIFF_TERM TRUE [get_ports {adc_dn_ip[0]}]
ところが、差動信号の IOSTANDARD や終端抵抗が他方と異なるはずはないので、N 側は省略することができます。また、P 側のピン配置が決まれば N 側も決まるので、N 側は一切書かなくても大丈夫なのです。書いても問題ないので好みのスタイルを選んでください。
XDC テンプレート
XDC のピン割り当ての方法を中心にざっと説明してきましたが、以上のことをまとめたテンプレートを用意しましたのでぜひ活用してください。
ピン番号とピン名を変えれば使えるはずです。
######################################################## # # XDCファイル テンプレート # ######################################################## ##### 一般のI/O(単一の信号) set_property PACKAGE_PIN D3 [get_ports single_signal] set_property IOSTANDARD LVCMOS18 [get_ports single_signal] ##### 一般のI/O(バス) set_property PACKAGE_PIN M5 [get_ports {bus_signal[0]}] set_property IOSTANDARD LVCMOS18 [get_ports {bus_signal[0]}] # バスのプロパティをまとめて設定する場合 set_property IOSTANDARD LVCMOS18 [get_ports {bus_signal[*]}] ################ よく使うIOSTANDARD #################### # LVCMOS12 LVCMOS15 LVCMOS18 LVCMOS25 LVCMOS33 LVTTL # TMDS_33 LVDS LVDS_25 # SSTL15 SSTL18_I SSTL18_II # HSTL_I HSTL_II HSTL_II_18 # DIFF_SSTL15 DIFF_SSTL18_I DIFF_SSTL18_II # DIFF_HSTL_I DIFF_HSTL_I_18 # DIFF_SSTL15_DCI DIFF_HSTL_I_DCI ######################################################## ##### LVCMOSにプルアップ/プルダウン/キーパー回路を使う場合 set_property PULLUP true [get_ports {exttrig_i[0]}] set_property PULLDOWN true [get_ports {exttrig_i[1]}] set_property KEEPER true [get_ports {exttrig_i[2]}] ##### (Kintex-7以上)HPバンクでLVDSを使う場合 set_property PACKAGE_PIN N6 [get_ports diffsig1_n] set_property PACKAGE_PIN N7 [get_ports diffsig1_p] set_property IOSTANDARD LVDS [get_ports diffsig1_p] set_property DIFF_TERM TRUE [get_ports diffsig1_p] ##### HRバンクでLVDSを使う場合 set_property PACKAGE_PIN W20 [get_ports diffsig2_p] set_property PACKAGE_PIN W21 [get_ports diffsig2_n] set_property IOSTANDARD LVDS_25 [get_ports diffsig2_p] set_property DIFF_TERM TRUE [get_ports diffsig2_p] ##### クロック周波数の設定 create_clock -name xtalclk_ip -period 20.0 [get_ports xtalclk_ip] ##### 一般配線をクロックに使う場合 set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets normal_signal] ##### コンフィグの高速化 set_property BITSTREAM.CONFIG.CONFIGRATE 66 [current_design] set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 2 [current_design] set_property BITSTREAM.CONFIG.UNUSEDPIN Pullup [current_design] set_property CFGBVS GND | VCCO [current_design] set_property CONFIG_VOLTAGE 1.8 | 2.5 | 3.3 [current_design]