Xilinx ISE におけるの制約の与え方 のバックアップ差分(No.1)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[公開メモ]]

#contents

* Xilinx ISE を使った FPGA 開発における制約の書き方と満たし方を勉強する [#fb7c992b]

FPGA や CPLD の開発では、回路の動作を HDL 言語で正しく記述するだけではだめで、
それが要求されるタイミングで動くことが必要になります。

FPGAの部屋 さん曰く、「XilinxのFPGAはHDLが書けても、
まだ半分しかマスターできたことにならないと思っている。」ということで、
動作クロックを高めなければならなかったり、
高速な周辺機器とやりとりをしなければならなかったりするときに重要になる、
「制約」の使い方について勉強する羽目になりました。

回路設計における「制約 (constraint)」は、

- 回路の動作速度やタイミングに対する要求をツールに伝える
- ツールが要求を満たすことができないときに、実装のヒント(配置の仕方など)を与える

の2つの目的で使われるようです。

以下では、私が初めて FPGA 開発をする際に使った種々の制約について、
考え方や、ツールの使い方をメモしたものです。

何せ初めてなので間違いなども多いかもしれません。
何かお気づきの点があればコメントをお願いします。

* 情報源 [#h05e2df2]

以下の内容は主に、FPGA の部屋 から学んだ内容に、私の経験をちょっとだけ加えたものです。

- FPGAの部屋 - UCFの書き方 ~
http://marsee101.web.fc2.com/ucf_kakikata.html ~

貴重な情報を公開して下さっている著者の方に深く感謝します。

* ピン配置および入出力タイプの制約 [#edbf7d8b]

すでに仕様の決まっている基板に対して 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)]

という手順です。

 NET "eth_txd[0]" LOC = J8;
 NET "eth_txen" LOC = D3;

というようなピン毎の設定の他、

 NET "eth_*" SLEW = FAST;

のように、* や ? などのワイルドカードを使った一括指定が可能です。

「これらのピンの中から適当なものを選んでくれ」という制約を与えるには、
LOC の後ろに可能なピンをカンマ区切りで与えます。

 NET "eth_txen" LOC = D3, D4, D5;

* クロック周期の制約 [#t846cbe7]

次に指定したくなるのがクロック周期に対する制約です。

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] に周期を入力します。

** DCM を使う場合のクロック周期制約 [#f7eae6ad]

ここで注意が必要なこととして、
DCM を使って生成されるクロック信号に対しては
クロック周期制約を掛けることができません。

えーと、できない、というのはちょっと語弊があって、
DCM に与えるクロックに制約を掛けておけば、
生成されるクロックに対しても自動的に制約がかかるため、
個別に制約を掛ける必要がない、というのが正しいです。~
参照 >> http://marsee101.blog19.fc2.com/blog-entry-1335.html ~

というわけで、1つだけ制約を掛けておけば、
そこから生成されるすべてのクロックに正しい制約が掛かることになります。

** クロックドメインをまたぐ信号を除外 [#ebac343f]

上記のように DCM で生成される複数のクロックに対して制約が掛かっている状況では、
ツールは関連するクロックドメイン間での信号の受け渡しについても、
律儀に遅延解析を行います。

通常、そのようなインタークロックな信号は、
受け渡しに特別な注意が必要になるため、
ロジック側でハンドシェイクその他の同期処理を入れて、
間違いのないデータの受け渡しを行うよう注意して HDL を書きます。

ところがツールはそのような高レベルの同期処理を理解できないため、
同期処理を入れなくても、遅延時間のみで正確な受け渡しのできる程度まで
クロック周期を落とすよう指示してきます。
このため、インタークロックな信号に対して遅延解析が行われると、
非常識なほど低いクロック周波数が算出されてしまいます。

そこで、クロックドメインをまたぐ信号について、
「遅延解析を行わない (TIG : Timing Ignore a net)」という制約(?)を
付けることで、正しい周波数解析が行えるようにします。

恐らく一番間違いのない方法は、
インタークロックの同期処理を行っている HDL ソースに
TIG 制約を埋め込む方法です。

例えば次のようにして、インタークロックな信号に TIG 制約を掛けることができます。

 LANG:verilog
 // クロックを越えて、トリガ信号を確実に伝えるためのモジュール
 // itrig に iclk に同期した正論理のトリガを入れると、
 // otrig に oclk に同期した1クロック幅のトリガを生じる
 
 module interclock_trig(
     input rst,
     input iclk,
     input itrig,
     input oclk,
     output otrig
 );
 
     (* TIG="TRUE" *) reg itrig_extended;
     (* TIG="TRUE" *) reg otrig0, otrig1;
     always @(posedge iclk)
         if(rst)
             itrig_extended <= 0;
         else
         if(itrig)
             itrig_extended <= 1;
         else
         if(otrig0)
             itrig_extended <= 0;
 
     always @(posedge oclk) begin
         if(itrig_extended)
             otrig0 <= 1;
         else
             otrig0 <= 0;
         otrig1 <= otrig0;
     end
     assign otrig = !otrig1 & otrig0;
 
 endmodule

あるいは、すべてのインタークロック信号に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 制約 [#ve7d7049]

インタークロックな信号以外にも、数クロック程度の遅延を許容できる信号、
例えば割り込み信号線や、他モジュールの起動信号線にも TIG 制約を掛けておくと
フィッターが無理してその線を短くしようとしなくなるため、
他の部分の性能が向上することが期待できます。

が、そう言った信号にはあらかじめレジスタで遅延を入れておいた方が、
遅延量が予測可能になる、不定値が伝搬しない、など、ずっと良いのかもしれません。

* 入出力のタイミング制約 [#w76621c2]

未稿

* 配置制約 [#of61a8f4]

上記のような形で、性能を要求するための制約を書き入れたら、
それを満たすように注意しながら HDL を記述していくのですが、
個々のモジュール単位、あるいはそれらを数個繋いだ段階では制約を満たせているのに、
繋ぐモジュールの数が増えていくと、制約を満たせなくなってしまうことが良くあります。

この理由としては、モジュール間の配線が長くなって遅延時間が増加していることが多いようです。

どこがボトルネックになっているかは、Place & Route 後に PlanAhead 
を起動すれば見ることができ、
同じ画面でボトルネック解消のためのヒントを与えることもできます。

ここで始めに何点か注意:

*** Synthesize Report の Timing Summary [#w0719d0b]

Synthesize が出してくる Report にも Timing Summary があるのですが、
繋ぐモジュールの数が増えてくるとこの値はあまり当てにはならないようです。

特に、上記のように DCM を使った回路では Synthesize が TIG を解釈しないので、
クロック周期などの見積もりがむちゃくちゃになってしまいます。

したがって、コンパイル時間が長く掛かってしまうのですが、
ちゃんとしたタイミング解析をするには Place & Route まで走らせなければなりません。

一方、モジュール単位のクロック周波数見積もりなどの用途では、
Synthesize Report の値も十分に参考になります。

*** Place & Route でエラーが出る [#h75af49c]

あまり無茶な制約が掛かっていると、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 レベルで回避策を講じることになります。

*** Trace & Route が完了したが、いくつかの制約が満たされないとき [#i7f403d5]

Design Summary/Report の [Design Overview] - [Summary] ページの
右上の Timing Constraints のところを見ると、
与えた制約がちゃんと満たされたかどうかが分かります。

うまく行っていれば All Constraint Met となりますが、~
そうでなければ 2 Failing Constraints などとなります。

その場合には、
Processes ペインの [Implement Design] - [Place & Route] - 
[Analyze Timing / Floorplan Design (PlanAhead)] をダブルクリックして、
HDL で実装した素子が FPGA 内にどのように配置されたか、また、
どのパスがクリティカルパスになっているかを確認し、
必要に応じて再配置することができます。~
http://marsee101.web.fc2.com/planahead.html

*** PlanAhead の基本的な使い方 [#xc7e392f]

未稿

* コメント [#f40e99f5]

#comment_kcaptcha


Counter: 103039 (from 2010/06/03), today: 3, yesterday: 0