Xilinx ISE におけるの制約の与え方 の履歴(No.19)
更新- 履歴一覧
- 差分 を表示
- 現在との差分 を表示
- ソース を表示
- 電気回路/HDL/Xilinx ISE におけるの制約の与え方 へ行く。
Xilinx ISE を使った FPGA 開発における制約の書き方と満たし方を勉強する†
FPGA や CPLD の開発では、回路の動作を HDL 言語で正しく記述するだけではだめで、 その回路がきちんと要求されるタイミングで動くことが必要になります。
FPGAの部屋 の marsee さん曰く、 「XilinxのFPGAはHDLが書けても、まだ半分しかマスターできたことにならないと思っている。」 とのことで、実際後半戦では FPGA 内部の構造まで踏み込んだ理解が必要だったり、 初心者にとってはいろいろと苦労が多いです。。。
ということで、動作クロックを高めなければならなかったり、 高速な周辺機器とやりとりをしなければならなかったりするときに重要になる、 「制約」の使い方について勉強する羽目になりました。
FPGA 回路設計での「制約 (constraint)」は、
- 回路の動作速度やタイミングに対する要求をツールに伝える
- ツールが要求を満たすことができないときに、実装のヒント(配置の仕方など)を与える
の2つの目的で使われるようです。
以下では、私が初めて FPGA 開発をする際に使った種々の制約について、 考え方や、ツールの使い方をメモしたものです。
いかんせん初めてなので、間違いなども多いかもしれません。 お気づきの点があればコメントをお願いします。
というか、まだ実機で試してない状況で書いているので、
鵜呑みにしないように(?!)お願いします。。。
(現在徐々に試しつつ、加筆・修正を加えています)
情報源†
以下の内容は主に、FPGA の部屋 で学んだ内容に、私の経験をちょっとだけ加えたものです。
- FPGAの部屋 - UCFの書き方
http://marsee101.web.fc2.com/ucf_kakikata.html
貴重な情報を公開して下さっている著者の marsee さんに深く感謝します。
ピン配置および入出力タイプの制約†
すでに仕様の決まっている基板に対して IP 設計を行う場合には、 始めからピン配置が決まっているので、それをツールに伝えます。
一方で、基板の設計と IP 設計とを同時に行う場合には、 大まかな位置だけを指定しておき、後はツールに任すこともできるようです。
制約を与えるには、
- プロジェクトの最上位モジュール (Top Module) に必要な input / output / inout ポートを実装
- ISE Project Navigator の Hierarchy ペインで Top Module を選択
- Processes ペインで [User Constraints] - [I/O Pin Planning (PlanAhead) - Pre-Synthesis] をダブルクリック
として GUI で行っても良いのですが、
http://marsee101.blog19.fc2.com/blog-entry-25.html
に書かれているとおり、.ucf ファイルをテキストとして編集することで、
一度に複数のピンに対して IO レベルを指定するようなことも可能です。
テキストで制約を与えるのであれば、
- プロジェクトの最上位モジュール (Top Module) に必要な input / output / inout ポートを実装
- ISE Project Navigator の Hierarchy ペインで Top Module を右クリック
- [New Source] から Implementation Constraints File を選んで、適当な名前でファイルを作成
- できた .ucf を選択し、Processes ペインで [User Constraints] - [Edit Constraints (Text)]
という手順です。
もちろん、すでにプロジェクトに .ucf ファイルが存在すれば、1.〜3. は必要ありません。
制約の書き方としては、
NET "eth_txd[0]" LOC = J8; NET "eth_txen" LOC = D3;
というようなピン毎の設定の他、
NET "eth_*" SLEW = FAST;
のように、* や ? などのワイルドカードを使った一括指定が可能です。
「これらのピンの中から適当なものを選んでくれ」という制約を与えるには、 LOC の後ろに可能なピンをカンマ区切りで与えます。
NET "eth_txen" LOC = D3, D4, D5;
クロック周期の制約†
次に指定したくなるのがクロック周期に対する制約です。
http://marsee101.blog19.fc2.com/blog-entry-26.html で触れられているように、 Timing Constraints ツールから設定するのが楽なようです。
起動方法は、
- Hierarchy ペインで .ucf ファイルをダブルクリック
です。
画面左の Constraint Type から Clock Domains を選択すると、 Unconstrained Clocks の部分にクロックの一覧が出るので、 ダブルクリックして [Clock signal definition] - [Specify time] - [Time] に周期を入力します。
マニュアルなどからすると、 コード中に制約を書くときの注意点 のように PERIOD 制約をコード中に埋め込むこともできるように読めるのですが、 これまでのところうまくできていません。
DCM を使う場合のクロック周期制約†
ここで注意が必要なこととして、 DCM を使って生成されるクロック信号に対しては クロック周期制約を掛けることができません。
えーと、できない、というのはちょっと語弊があって、
DCM に与えるクロックに制約を掛けておけば、
生成されるクロックに対しても自動的に制約がかかるため、
個別に制約を掛ける必要がない、というのが正しいです。
参照 >> http://marsee101.blog19.fc2.com/blog-entry-1335.html
というわけで、1つだけ制約を掛けておけば、 そこから生成されるすべてのクロックに正しい制約が掛かることになります。
クロックドメインをまたぐ信号を除外する†
上記のように DCM で生成される複数のクロックに対して制約が掛かっている状況では、 ツールは関連するクロックドメイン間での信号の受け渡しについても、 律儀に遅延解析を行います。
例えば、次のようなプログラムでは、100MHz のクロックから 66MHz のクロックに信号を渡しています。
LANG:verilog always @(posedge clk_100MHz) sig <= some_expression; always @(posedge clk_66MHz) if(sig) some_proc();
通常、このようなインタークロックな信号は、 受け渡しに特別な注意が必要になるため、 ロジック側でハンドシェイクその他の同期処理を入れて、 間違いのないデータの受け渡しを行うよう注意して HDL を書きます。
ところがツールはそのような高レベルの同期処理を理解できないため、 同期処理を入れなくても、遅延時間のみで正確な受け渡しのできる程度まで クロック周期を落とすよう指示してきます。 このため、インタークロックな信号に対して遅延解析が行われると、 非常識なほど低い最高クロック周波数が算出されてしまいます。
この信号パスの許容遅延時間は 10ns 周期と 15ns 周期との最小公約数である 5ns しかなくなりますので、達成するのが非常に難しくなります。
そこで、クロックドメインをまたぐ信号について、 「遅延解析を行わない (TIG : Timing Ignore a net)」という制約(?)を 付けることで、不必要な遅延解析を省き、正しい周波数解析が行えるようにします。
TIG の指定の仕方(コードに埋め込む)†
TIG の指定方法として一番簡単なのは、 インタークロックの同期処理を行っている HDL ソースに TIG 制約を埋め込むことです。上記の例で言えば、次のようにします。
LANG:verilog (* TIG="TRUE" *) reg sig;
これで、sig の出力を受けるパスについて遅延解析が行われなくなります。
注意点としては、このようにコード中に TIG を書く際には、 その信号線がモジュール外に出て行かないようにすべきであるという点があります。 電気回路/HDL/コード中に制約を書くときの注意点 にも書きましたが、 コード中に埋め込む TIG 制約は、信号源に付与されるため、input wire や output reg に TIG を付けてしまうと、思った以上に広い範囲に TIG の効力が及んでしまい、本来ちゃんと遅延解析が行われなければならないパスまで 遅延解析が行われないという結果を招きかねません。
さらに、同じく上記のリンクに書かれているとおり、parameter を持つ module 中で TIG を使うと、正しく動作しないこともあるようです。 (ISE 11.5 および 12.1 で確認)
したがって、コード中で TIG を付けるのは module 内に閉じたパスに対してのみ、 と決めておいた方が無難なようです。
TIG の指定の仕方(.ucf に書く)†
.ucf ファイルに TIG を指定するときには、 パスの指定を FROM 〜 THROUGH 〜 TO で行えるため、 思った以上に広い範囲に TIG が及んでしまうというミスを防ぐことができます。
やり方は、まず TNM でネットに名前を付けておき、 TIMESPEC に FROM 〜 THROUGH 〜 TO でパスを指定して TIG を付ければ良いようです。
複数のネットに一括で制約を掛けたければ、下のようにワイルドカード "*" をうまく使えるよう、インスタンス名を工夫しておくと良いようです。
LANG:xcf INST "*bits[*].double_ff_1bit/double_ff_register_first" TNM = "double_ff_register"; TIMESPEC TS_DOUBLE_FF = TO "double_ff_register" TIG;
この例の詳しい説明は 電気回路/HDL/コード中に制約を書くときの注意点 にあります。
プロジェクトコード内にはなるべく TIG を書かない†
そもそもインタークロック信号の扱いには細心の注意を払う必要があるため、 TIG が必要となるような箇所には場当たり的な対処を行うのではなく、 実績のあるライブラリ (電気回路/HDL/非同期信号を扱うための危ういVerilogライブラリのような危うい物では無く?!) を用いるのが得策です。 そして、そのような非同期信号用のライブラリコード内で TIG 制約が記述されていれば、 個別のコードやプロジェクトに TIG を記述することはほとんど無くなります。
プロジェクトコードに TIG を書かなければならないときは、本当にそれが必要なのか、 また、それで正しく動くのか、をよく考えてからにすべきだと思います。
インタークロックなネットに一括で TIG を掛ける†
どうしても、すべてのインタークロック信号に1つ1つ TIG 制約を掛けるのが面倒、 という場合には、http://marsee101.blog19.fc2.com/blog-entry-27.html のように 一括で TIG 制約を掛ける方法もあるそうです。
LANG:verilog BUFG clk_125MHz_bufg ( .I(clk_125MHz_pre), .O(clk_125MHz)); BUFG clk_100MHz_bufg ( .I(clk_100MHz_pre), .O(clk_100MHz)); IBUFG clk_125MHz_in_ibufg ( .I(clk_125MHz_in), .O(clk_125MHz_in1)); DCM_SP #( .CLKDV_DIVIDE(1.0), // Divide by: 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5 // 7.0,7.5,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0 or 16.0 .CLKFX_DIVIDE(5), // Can be any integer from 1 to 32 .CLKFX_MULTIPLY(4), // Can be any integer from 2 to 32 .CLKIN_DIVIDE_BY_2("FALSE"), // TRUE/FALSE to enable CLKIN divide by two feature .CLKIN_PERIOD(8.0), // Specify period of input clock .CLKOUT_PHASE_SHIFT("NONE"), // Specify phase shift of NONE, FIXED or VARIABLE .CLK_FEEDBACK("1X"), // Specify clock feedback of NONE, 1X or 2X .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), // SOURCE_SYNCHRONOUS, SYSTEM_SYNCHRONOUS or // an integer from 0 to 15 .DLL_FREQUENCY_MODE("HIGH"), // HIGH or LOW frequency mode for DLL .DUTY_CYCLE_CORRECTION("TRUE"), // Duty cycle correction, TRUE or FALSE .PHASE_SHIFT(0), // Amount of fixed phase shift from -255 to 255 .STARTUP_WAIT("FALSE") // Delay configuration DONE until DCM LOCK, TRUE/FALSE ) DCM_inst ( .CLK0(clk_125MHz_pre), // 0 degree DCM CLK output .CLKFX(clk_100MHz_pre), // DCM CLK synthesis out (M/D) .CLKFB(clk_125MHz), // DCM clock feedback .CLKIN(clk_125MHz_in1), // Clock input (from IBUFG, BUFG or DCM) .PSEN(1'b0), // Dynamic phase adjust enable input .RST(1'b0) // DCM asynchronous reset input );
のようにして生成したクロックに対して、.ucf に次のように書きます。
PIN "DCM_inst.CLK0" TNM = "CLK125MHz"; PIN "DCM_inst.CLKFX" TNM = "CLK100MHz"; TIMESPEC TS_IGNORE1 = FROM "CLK125MHz" TO "CLK100MHz" TIG ; TIMESPEC TS_IGNORE2 = FROM "CLK100MHz" TO "CLK125MHz" TIG ;
ここでは、DCM_inst の、CLK0 および CLKFX ピンにそれぞれ CLK125MHz、 CLK100MHz という名前をつけ、CLK125MHz から CLK100MHz、あるいは逆の CLK100MHz から CLK125MHz への信号すべてに TIG 制約を掛けています。
可能ではありますが、やはり個別に TIG を掛けるのが正しいスタンスだと感じています。
その他の信号への TIG 制約†
インタークロックな信号以外にも、数クロック程度の遅延を許容できる信号、 例えば割り込み信号線や、ステートマシンの起動用信号線等にも TIG 制約を掛けておくと フィッターが無理してその線を短くしようとしなくなるため、 他の部分の性能が向上することが期待できます。 しかし、そのような用途に TIG 制約を使うのは、やはりあまりお勧めできません。
というのも、そう言った目的にはあらかじめレジスタで遅延を入れておくことで 同じ効果が得られるからです。レジスタの挿入で解決した方が、 遅延量が予測可能になる、不定値が伝搬する問題を生じない、など、 ずっと良い結果が得られます。
そのような場合に挿入するレジスタには、性能向上のために REGISTER_DUPLICATION 制約や、REGISTER_BALANCING 制約を付けることも検討の余地がありそうです。
配置制約†
クロック周期に対する制約は、 個々のモジュール単位、あるいはそれらを数個繋いだ段階では制約を満たせているのに、 繋ぐモジュールの数が増えていくと、制約を満たせなくなってしまうことが良くあります。
この理由としては、モジュール間の配線が長くなって遅延時間が増加していることが多いようです。
どこがボトルネックになっているかは、Place & Route (PAR) 後に PlanAhead を起動すれば見ることができて、 同じ画面でボトルネック解消のためのヒント(配置制約)を与えることもできます。
ここで始めに何点か注意:
Synthesize Report の Timing Summary†
Synthesize が出してくる Report にも Timing Summary があるのですが、 繋ぐモジュールの数が増えてくると、この値はあまり当てにはならないようです。
特に、上記のように DCM を使った回路では Synthesize が TIG を解釈しないので、 クロック周期などの見積もりがむちゃくちゃになってしまいます。
したがって、コンパイル時間が長く掛かってしまうのですが、 ちゃんとしたタイミング解析をするには Place & Route (PAR) まで走らせなければなりません。
一方、モジュール単位のクロック周波数見積もりなどの用途では、 Synthesize Report の値も十分に参考になります。
で、参考にする際に知っておくと良いのは、Synthesize Report の出力する最高クロック周波数はあくまで概算なので、 実際にはこの値よりも「高い」周波数で動作するよう合成することも可能です。
ぎりぎりまでしぼってそれでも周波数があと少しだけ上がらない、 という状況であれば、配置の工夫で周波数を上げることができる可能性が高いので、 Synthesize の段階ではあまり気にせず Place & Route まで進んでみるのも良いようです。
Place & Route でエラーが出る†
あまり無茶な制約が掛かっていると、Place & Route がエラーで止まってしまいます。
ERROR:Par:228 - At least one timing constraint is impossible to meet because component delays alone exceed the constraint. A timing constraint summary below shows the failing constraints (preceded with an Asterisk (*)). Please use the Timing Analyzer (GUI) or TRCE (command line) with the Mapped NCD and PCF files to identify which constraints and paths are failing because of the component delays alone. If the failing path(s) is mapped to Xilinx components as expected, consider relaxing the constraint. If it is not mapped to components as expected, re-evaluate your HDL and how synthesis is optimizing the path. To allow the tools to bypass this error, set the environment variable XIL_TIMING_ALLOW_IMPOSSIBLE to 1.
曰く「制約が無茶なので合成を頑張る気も起きない」と言うわけです。
解説すると、
信号線の遅延時間は(素子自体の遅延)+(信号伝達の遅延)で決まります。
このうち素子の配置を工夫して減らせるのは(信号伝達の遅延)の方だけなわけですが、 あまりに制約が厳しいと(素子自体の遅延)だけで、 要求される遅延時間を超えてしまいます。
この場合、どんなに配置を工夫しても制約を満たすのは無理ですので、 HDL 記述のレベルで回路を見直す必要があります。 上記エラーはそのような状況を示しています。
で、どうやって見直すかというと、、、
上記のエラーメッセージの直後に、満たせなかった制約の一覧が出ますが、 それだけだと何が悪いか分かりません。
Processes ペインの [Implement Design] - [Map] - [Generate Post-Map Static Timing] - [Analyze Post-Map Static Timing] をダブルクリックして、Timing Analyzer を起動します。
エラーとなったパスに赤のチェックが付いていますので、 その内容から HDL レベルで回避策を講じることになります。
Xilinx の「スピード向上のためのストラテジ」:
http://www.xilinx.com/itp/xilinx8j/help/iseguide/html/ise_xst_speed_strategies.htm
のような情報もあるにはあるのですが、、、
実際にはツールに頼るだけだとコンパイル時間が長くなるだけであまり良い結果は得られないため、 クリティカルパスにレジスタを挿入するとか、レジスタ挿入の位置を工夫するといった、 回路に合わせた人為的な最適化をした方がうまく行くように感じられます。
ただまあ、
- Equivalent Register Removal
- Register Duplication
- FSM Encoding Algorithm
- Register Balancing
あたりはかなり効果がある場合も多いので、 見てみると良いこともあるようです。
Trace & Route が完了したが、いくつかの制約が満たされない†
Trace & Route が完了した後、 Design Summary/Report の [Design Overview] - [Summary] ページの 右上の Timing Constraints のところを見ると、 与えた制約がちゃんと満たされたかどうかが分かります。
うまく行っていれば All Constraints Met となりますが、
そうでなければ 2 Failing Constraints などとなります。
この状況は、「素子の配置を工夫すれば制約を満たせる可能性がある」ということで、 配置制約の出番になります。 ただ細かな配置制約を与えて、何とかタイミング制約を満たしたとしても、 少しの回路変更でまたうまく行かなくなったりするので、 もし HDL の見直しで改善できる余地があるなら、 配置をいじるよりもそちらを優先すべきです。
一方で、以下の例に挙げるような「大まかな配置制約」は、 細かな回路変更により破綻することも少ないので、 一考の余地があると思います。
PlanAhead の基本的な使い方†
Trace & Route でいくつかの制約が満たされないという結果になった時、
PlanAhead というツールでその原因を探り、
解決のヒントとして配置制約を与えることができます。
http://marsee101.web.fc2.com/planahead.html
起動方法は、Processes ペインの [Implement Design] - [Place & Route] - [Analyze Timing / Floorplan Design (PlanAhead)] をダブルクリック、です。
制約を満たさなかったパスは左下に赤文字で出ます。 それをクリックすると右上の領域に白い矢印で該当パスが表示されます。 ツールバーの緑で囲った部分を使って拡大すると、 1つ1つの素子の配置まで見ることができます。
遅延の大きいパスは大抵非常に長距離を走っているので、 この距離が小さくなるように素子の配置を変えてやるのが、 ここからの仕事です。
真ん中の青丸で囲ったところに "Create Site Constraint Mode" というのがあります。、 これをクリックしてから素子をドラッグ・ドロップすると、 特定の素子の位置を望みの箇所に固定することができます。
「素子を動かして矢印の長さを短くする」というだけなら簡単そうなんですが、 それらの素子には矢印で表示されたクリティカルパス以外にもいくつものパスが繋がっていて、 あちらを短くするとこちらが長くなる、というような状況になっていることが多く、 言葉で言うほど簡単ではないです。
一番下のタブで I/O ポート位置なども表示できるので、 それらを参考に全体を見渡しながらうまい配置を思い浮かべることになります。
ここではブロックメモリの位置を固定することで、 全体の配置を調整することを試みました。
ドラッグ中の素子には、その素子へ、 あるいはその素子からのパスがたくさん表示されます。 本来これらがどこに下ろすかの指標になるのですが、、、
今回は大まかな位置だけ決めて、 残りの細かい素子はおおきく自動で再配置してもらおうという算段なので、 線の長さはあまり気にしませんでした。
制約により固定された素子はオレンジで表示されています。
気の向くままにいじったら制約を保存して、 もう一度 Place & Route します。
上記の操作により ucf ファイルに追加された制約は次のようになりました。
INST "trimac/rx/rxmem/Mram_data2" LOC = RAMB16_X0Y17; INST "writer/rxfifo/rxfifo/mem" LOC = RAMB16_X1Y19; INST "trimac/rx/rxmem/Mram_data1" LOC = RAMB16_X0Y18; INST "trimac/control/Mrom__varindex0000" LOC = RAMB16_X0Y19; INST "reader/txfifo/txmem/mem" LOC = RAMB16_X0Y20; INST "reader/rxfifo/bmem/mem" LOC = RAMB16_X1Y18; INST "trimac/tx/Mram_mem" LOC = RAMB16_X0Y21;
上記制約を与えてフィッティングした結果です。
今回はメモリの配置で大まかな各モジュールの位置を指定するだけで、 残りの素子はツールによる自動配置に任せたまま全ての制約をクリアすることができました。
いつもそううまく行くわけではないのでしょうが、 基本としては最小限のプリミティブに制約を掛けておき、 残りの配置をツールに最適化させるのが賢い使い方だと思います。
配置指定をテキストベースで行う†
上記の方法では、要素の位置を完全に一カ所に決定する制約を与えましたが、 ucf ファイルをテキストベースで編集する場合には、
INST "trimac/rx/rxmem/Mram_data2" LOC = RAMB16_X0Y21,RAMB16_X0Y20,RAMB16_X0Y19;
のようにして、列挙された3カ所のどこか、という制約を与えたり、
INST "trimac/rx/rxmem/Mram_data2" LOC = RAMB16_X0Y16:RAMB16_X1Y21;
のようにして、左下と右上とで囲まれる四角形の領域の中、という制約を与えることもできます。
そのようなゆるい制約にしておけば、 その範囲内でのツールによる最適化を期待できるのですが、 次に紹介する点には注意が必要です。
気をつける点†
テキストベースで上記のような幅を持った制約を与えた場合、PlanAhead がその制約を解釈してくれないので、 PlanAhead で制約を追加や変更して上書きすると、テキストで与えた制約が失われてしまうようです?
回避策?†
ucf ファイルを2つ作って、ツールに任せるものと手で編集するものとに分けて使えば良いんですかね。
main.ucf と main_by_hand.ucf を作っておくと、 PlanAhead の起動時にどちらを読み込むか問い合わせてくるので、 main.ucf を選んでやれば main_by_hand.ucf の記述がいつの間にか消えてしまうという現象を避けられそうです。
入出力のタイミング制約†
参照> http://marsee101.blog19.fc2.com/blog-entry-26.html
入力信号のセットアップ&ホールド時間制約†
OFFSET = IN 2.5 ns VALID 3.0 ns BEFORE "eth_rxclk" RISING;
の形で、eth_rxclk に同期して入力される全てのピンに対して、 最低限 2.5 ns のセットアップ時間と 3.0 - 2.5 = 0.5 ns のホールド時間で動作するよう制約を掛けることができます。
出力信号のクロックエッジからの遅延時間制約†
OFFSET = OUT 6 ns AFTER "clk_125MHz_in" REFERENCE_PIN "eth_gtxclk" RISING;
の形で、clk_125MHz_in に同期して(clk_125MHz_in から DCM で生成されるいずれかのクロックに同期するものも含む) 出力されるすべての信号は eth_gtxclk の RISING EDGE から 6 ns 以内に値が決定しなければならない、という制約を与えます。
同じクロックに同期して出力される信号の中から特定のネットを指定して制約を掛けるには、
NET "eth_tx*" OFFSET = OUT 6 ns AFTER "clk_125MHz_in" REFERENCE_PIN "eth_gtxclk" RISING;
あるいは、
INST "eth_tx*" TNM = PADGROUP_ETH_TX; TIMEGRP "PADGROUP_ETH_TX" OFFSET = OUT 5 ns AFTER "clk_125MHz_in" REFERENCE_PIN "eth_gtxclk" RISING;
の形を使います。
NET を使った場合と TIMEGRP を使った場合との違いは、 NET を使った場合にはワイルドカードを展開した結果、 複数の制約として認識されるため、Timing Analyzer 上で非常に多くの制約が表示されてしまうのに対して、 TIMEGRP を使った方では1つの制約になることが挙げられます。
手動ではなく GUI の Constraints Editor を使う場合には NET 指定時のワイルドカードを認識してくれないので、
INST "eth_txd[0]" TNM = PADGROUP_ETH_TX; INST "eth_txd[1]" TNM = PADGROUP_ETH_TX; INST "eth_txd[2]" TNM = PADGROUP_ETH_TX; INST "eth_txd[3]" TNM = PADGROUP_ETH_TX; INST "eth_txd[4]" TNM = PADGROUP_ETH_TX; INST "eth_txd[5]" TNM = PADGROUP_ETH_TX; INST "eth_txd[6]" TNM = PADGROUP_ETH_TX; INST "eth_txd[7]" TNM = PADGROUP_ETH_TX; INST "eth_txen" TNM = PADGROUP_ETH_TX; INST "eth_txer" TNM = PADGROUP_ETH_TX; TIMEGRP "PADGROUP_ETH_TX" OFFSET = OUT 5 ns AFTER "clk_125MHz_in" REFERENCE_PIN "eth_gtxclk" RISING;
のようにすべてのネットを列挙する形で制約を作成することになるようです。
一応 PlanAhead とは違って、対応していない制約も表示だけは可能ですし、 上書き保存時に、ツールが非対応の制約が消えてしまうことも無いようです。
I/O バッファー制約†
入出力タイミング制約を満たすには、IBUF あるいは OBUF と呼ばれる入出力ピンに内蔵された(?) フリップフロップを活用するのが近道なようです。このための制約が IOB 制約と呼ばれます。
Verilog では、
LANG:verilog (* IOB="FORCE" *) reg some_register;
などと書くことで、特定のレジスタを IBUF あるいは OBUF として実装することができます。
IOB="TRUE" という書き方もできるのですが、"FORCE" としておけばレジスタを IOB に入れることができなかったときにエラーとなるので、IOB を使っているつもりでいたのに実は使えていなかった、というようなミスを防ぐことができます。
他にも遅延制約など、いろいろテクニックがありそうなのですが、 さしあたっては以上の方法でうまく行ってしまったようなので、 また必要になったときに書き足します。
指定した制約が思った通りに掛かっているかどうかの確認方法†
コード中や、.ucf ファイルに書いた制約は、map 時に .pcf ファイルにまとめられます。
この .pcf ファイルを見ることで、指定した制約が思った通りのオブジェクトに対して、 思った通りの形でかかっているかどうかを確認できます。
特に、コード中に制約を記載した場合や NET で制約の対象を指定した場合、 制約のかかる範囲が思った以上に広い場合などがあるので、 慣れていない指定をするときは .ucf を直しては .pcf ファイルを見返すようなトライアンドエラーが、 制約の書き方を学ぶ上で助けになるようです。
#マニュアルを読んでもどうもピンと来ないので(困
ucf ファイル中で Net 名を簡単に指定するには†
Synthesis のオプションで Netlist Hierarchy を Rebuild にしておくと、 最適化で構成がいろいろ変わった場合にも、もともとの Net 名で Netlist が出力されるため、.ucf に記述する制約ももともとの Net 名で指定できるようになります。
そうでないと、例えば main/sub1/sub2 というインスタンスの pin1 というピンに接続されたネットに対して制約を掛けたい場合、 Synthesize 後にそのネットに付く名前を一生懸命探さないと制約を掛けることができません。
たとえば、上記ピンに main/sub4 という FF の出力が繋がれている場合、 実際のネット名は "main/sub4" とか、"main/sub4_1" とかになって、 main/sub1/sub2.pin1 に制約を掛けたいにもかかわらず、この名前で 制約を書かなければならなくなります。
上記オプションを As Optimized から Rebuild にしておくと、
NET "main/sub1/sub2/pin1" TNM = "sub2_pin1";
のようにして、ソースコード中に現れる信号名そのもので制約を書くことができるようになります。
このオプションがなぜデフォルトで Rebuild になっていてくれないのか、 非常に疑問です。
指定した制約が合成に反映されていないように思えるとき†
はまったのでメモ。
ucf ファイルに
LANGUAGE:ucf TIMESPEC TS_DOUBLE_FF = TO "double_ff_register" TIG;
と書いてあるのに、対応する pcf に TIG の指定が現れない。
何で無視するの〜! っと悩んでいたところ、この行をコピペして作った別の行で TIMESPEC の識別名が重複してました。
LANGUAGE:ucf TIMESPEC TS_DOUBLE_FF = TO "some_other_net" TIG;
このような場合、Translate で
WARNING:NgdBuild:1345 - The constraint <TIMESPEC TS_DOUBLE_FF = TO "double_ff_register" TIG;> [src/main.ucf(4)] is overridden by the constraint <TIMESPEC TS_DOUBLE_FF = TO "some_other_net" TIG;> [src/main.ucf(64)]. The overriden constraint usually comes from the input netlist or ncf files. Please set XIL_NGDBUILD_CONSTR_OVERRIDE_ERROR to promote this message to an error.
というワーニングが出るのですが、エラーにならずに「指定が上書きされる」ため、 原因不明のまま前に指定した制約がかからず悩むことになります。
ワーニングに書かれたとおり、重複する制約識別名をエラーにするよう、 ndgbuild に XIL_NGDBUILD_CONSTR_OVERRIDE_ERROR を渡せばよいとのことです。
とりあえず、「マイコンピュータ」を右クリックからプロパティで、 「システムの詳細設定」-「詳細設定」-「環境変数」-「ユーザー環境変数」に
- 変数名:XIL_NGDBUILD_CONSTR_OVERRIDE_ERROR
- 変数値:1
を追加して Project Navigator を再起動したところ、ちゃんとエラーになりました。
でも、これってシステムレベルで設定しなきゃならないんですかね???
さすがに何か方法があるんじゃないかと思うのですが、まだ探せてません (TT
まあ、ISE をアップデートしたときに指定し忘れるのも痛いので、 システムレベルの設定というのもあながち間違いではないのかもしれませんが・・・
そもそもこんなオプションがあるのにデフォルトが off になっているとか、 意地悪なんだから、もう。
TNM / TNM_NET について†
信号線にタイミング制約を掛けるには、まず TNM や TNM_NET で信号線をグループ化して、 そのグループに対して制約を掛けることになります。
TNM と TNM_NET の違い†
「制約ガイド v13.3」によると、
- TNM および TNM_NET
- TNM は同じネットに駆動されるフリップフロップ、ラッチ、RAM またはパッドのネット グループに付けます。
- TNM は IBUF または BUFG コンポーネントを通らず、入力パッドまで到達します。
- TNM_NET 制約は IBUF およびグローバルクロックバッファを通ります。
- ザイリンクスでは、次を推奨しています。 – インスタンスおよびマクロ(階層ブロック)をグループにするにはTNMを使用します。 – 入力パッドをグループにするには、パッドに駆動されるネットにTNMを使用します。 – 同じネットに駆動されるクロック、クロックイネーブル、チップイネーブル、読み出 し/書き込み、リセットなどの複数のロジック エレメントをグループにするには、 TNM_NET を使用します。
とのことです。
IBUF や BUFG を越えて影響が及ぶか、越えないか、の違いのようです?
TNM/TIMEGRP 制約の使い方†
制約ガイドがわかりにくすぎるのですが、何とか理解しようと試みているところです。
Constraints Guide - UG625 (v. 13.4) January 18, 2012
http://www.xilinx.com/support/documentation/sw_manuals/xilinx14_2/cgd.pdf
Timing Closure User Guide - UG612(v 13.4) January 18, 2012
http://www.xilinx.com/support/documentation/sw_manuals/xilinx14_2/ug612.pdf
.ucf ファイルで指定することを想定しています。
TNM/TNM_NET 制約は複数のオブジェクトを1つのタイミンググループとして まとめて呼べるようにするために使われる物です。 TIMESPEC 制約などではオブジェクト名を直接指定できず、 タイミンググループを指定して記述しなければならないため、
- TNM/TNM_NET でグループ名を定義する
- TIMEGRP でグループ同士の結合や引き算ができる
- TIMESPEC でグループ名を使って実際の制約を記述する
という段階を踏むことが必要となるのですが、そもそもの1段階目から 目的のオブジェクトを選択できずに困ることが多かったりします(泣
上でも 書きましたが、目的のオブジェクトを正しくグループ化できているかどうかは、 Translate 時の警告・エラーを注視し、Map 時に作成される .pcf ファイルも参照しながら確認します。 指定した TNM がまったくどのオブジェクトにもマッチしない場合、 Translate がエラーで止まる場合と、警告のみ出して論理合成を継続する場合、 さらには警告すら発しない場合があるようです???
そこで、いざというときは .pcf ファイル頼みになってしまいます。 .pcf ファイルはソースコードや複数の.ucfに分散して書かれた制約を まとめ直したファイルで、Place and Route (PAR) は .pcf ファイルのみを 見て実装しますので、ここに正しく書かれていない制約は実装される回路に反映されません。
TNM/TNM_NET 文法†
とりあえずの基本は、
(名前を付ける対象物) TNM = "指定した対象物に付けるグループ名"; (名前を付ける対象物) TNM_NET = "指定した対象物に付けるグループ名";
の形になるようです。
同じグループに複数の対象物を入れたいときには、対象物の指定時にワイルドカード "*" を使うか、 あるいは同じグループ名で TNM 制約を複数書けば良いようです。複数指定した TNM 制約に 書かれたすべての対象が同じグループに所属することになります。
名前を付けることのできる対象物としては、
- ネット : 指定されたネットで駆動されるすべてのクロック同期要素(フリップフロップ、ラッチ、RAM、CPU、HSIOS、MULT) および PAD がグループに入ります。ただし、TNM 制約では IBUF, BUFG の先は対象となりません。TNM_NET 制約では IBUF, BUFG の先まで含めて対象となります。クロックネットを指定するときは TNM_NET を使うと良いことが多いはずです。TNM_NET は "ネット" にしか適用できません。その他の物、プリミティブやピンを指定した場合、警告を出しつつ無視するそうです。
- プリミティブやマクロ要素のピン : 指定されたピンから順方向にたどったすべてのパスが繋がるクロック同期要素が対象になります、と書かれていますが、それは入力ピンの時だけかもしれません?少なくともマクロ要素の入力ピンを指定した場合には、そのネットを入力する要素内要素すべてが対象となります。プリミティブやマクロ要素の出力ピンを指定した場合にはどこまでが対象となるのか・・・未確認です。
- プリミティブ要素 : 要素自体が対象になります
- デザイン要素 : 指定された要素に含まれるすべての子孫要素が対象になります
ただし、対象として
- IBUF を指定することはできません
- IBUF の出力ピンを指定した場合は、そこから先が対象になります
それぞれ、
NET "some/net" TNM = {optional qualifier} "group_name"; PIN "some/inst.pin" TNM = {optional qualifier} "group_name"; INST "some/inst" TNM = {optional qualifier} "group_name"; NET "some/net" TNM_NET = {optional qualifier} "group_name";
のように指定します。
{optional qualifier} として、FFS, PADS, DSPS, RAMS, MULTS, HSIOS, CPUS, LATCHES, BRAMS_PORTA, BRAMS_PORTB のどれかを指定することできて、名前でマッチする対象物のうち、指定したタイプに該当する物だけに対象を絞り込むことができます。
NET名やPIN名などにはワイルドカードを指定できます。
- "*" は 0 文字以上の任意の文字列にマッチします
- "?" は 1 文字の任意文字にマッチします
- "something/*" だと最後の "*" はそれ以下のすべての階層にマッチします
- "something/*/" の組み合わせでは "*" はデザイン階層の1階層分だけにマッチします
よく分からない指定の仕方†
NET clock_enable TNM_NET = RAMS(address*) fast_rams;
This time group (fast_rams) is the RAM components driven by net name of clock_enable with an output net name of address*
とか、
The following time group is created with a search string and a predefined group of FFS in a Multi-Cycle constraint.
TIMESPEC TS01 = FROM RAMS TO FFS(macro_A/Qdata?) 14.25ns;
The destination time group is based upon flip flop components with an output net named macro_A/Qdata?
という例が載っているのですが、これがどういう指定方法なのか、 ほとんど書かれていないので詳しい説明を捜索中です。
TIMEGRP 制約†
名前を付ける以外の機能もあるのですが、 ここでは新しいタイミンググループを定義する方法に絞って解説します。
TIMEGRP "group_name" = "group1" "group2" "group3" ... TIMEGRP "group_name" = "group1" EXCEPT "group2" TIMEGRP "group_name" = RISING "clk_group1" TIMEGRP "group_name" = FALLING "clk_group1"
それぞれ、
- 既存グループを結合する
- 既存グループから別グループを除外する
- あるクロックネットのうち RISING エッジを使っているもののみを取り出す
- あるクロックネットのうち FALLING エッジを使っているもののみを取り出す
ということのようです。
AND を取る機能は無いんでしょうか???
タイムグループ名を制約に使うときの注意点†
ネットを指定した TNM は、「そのネットを入力として読み取るすべてのクロック同期要素(ラッチ等)」をグループ化します。
したがって、そのようなグループを FROM/THRU/TO の TO に指定するのはアリですが、 FROM や THRU に指定するのはあり得ないことになります。
逆に、FROM や THRU で指定するのは INST に付けた TNM になるのだと思います。
特に THRU で指定できるのは、TNM を使う限りクロック同期ではない LUT などの組み合わせ回路要素のみになるのだと思うのですが・・・THRU に NET 名を使う例として、
NET $3M17/On_the_Way TPTHRU = abc; TIMESPEC TS_mypath = FROM my_src_grp THRU abc TO my_dest_grp 9 ns;
というのが載っていました。TNM の代わりに TPTHRU を使うと良いようですね。
しかしまあ†
もっとちゃんとしたドキュメントが無いのはかなりひどいと思う。
コメント†
- こんにちは。紹介していただいてありがとうございます。私は、筑波大のシス情技術室所属です。 -- [marsee]
- どうもこんにちは。そうだったのですね。こちらは始めたばかりの初心者で、勉強々々の毎日です(汗 近くのよしみで(?)今後ともどうぞよろしくお願いいたします。 -- [武内(管理人)]
- こんにちは。こちらこそよろしくお願いいたします。よろしければ、そのうちに、直接お会いして、情報交換などは、いかがでしょうか? -- [marsee]
- こちらからは交換できるほどの情報はないのですが、せっかくのつながり、と思っていただけるのでしたらぜひお願いします( >そのうちに、直接 )左上の「連絡先」のメールアドレスにメールを、あるいは内線でも、お時間のあるときにご連絡下さい。 -- [武内(管理人)]