制約条件でタイミングを制御する

学習のポイント

論理合成の基本的な制約条件(Constraints)とその設定方法を覚えてください。ここで紹介するのは論理合成の制約条件を設定するときに、最低限必要なコマンドです。たくさんでてきますがどれも重要です。

1. 動作環境の設定 (set_operating_conditions)

LSIの動作能力は、電源電圧、動作温度、プロセスパラメータによって大きく変わります。設計する際には、これらの条件が変動する範囲内で目標性能を発揮するようにしなければなりません。論理合成する際も、そのLSIが使用される環境における「最悪条件」のもとでも要求性能を発揮する回路にする必要があります。

# 動作環境の設定コマンド
set_operating_conditions MAX567 -library hd350s

例えば「電源電圧3.3V±0.3V、動作温度範囲-20~100℃」という仕様が与えられたとします。この場合、最悪条件として MAX567 という動作環境を選択し、上記のように設定します。

LSIライブラリ (例: hd350s) に用意されている動作環境のパラメータ

電源電圧
変動範囲
動作温度
変動範囲
プロセスパラメータ
製造ばらつき
↓
最悪条件 (ワーストケース) : MAX567
この条件で要求性能を満たせば、どんな環境でも動作する
⚠️ 注意

動作環境を設定せずに論理合成すると、出来上がった回路は要求仕様を満たしていない可能性があります。この場合には全く無意味な論理合成になってしまいますので、動作環境は必ず設定しましょう。(ライブラリの中の環境一覧は report_lib コマンドで調べることができます)

2. 配線遅延モデルの設定 (set_wire_load_model)

論理合成ツールは回路性能を評価する際、ゲート遅延とともに「配線遅延」の情報を必要とします。しかし合成段階では実際のレイアウト(配線長)は不明なため、ライブラリ内に用意されている配線情報の仮想モデル(配線遅延モデル)を使用して計算します。

# 配線遅延モデルの設定コマンド
set_wire_load_model -name "7000_GATE_MODEL"

ライブラリの中には回路規模に応じた複数の配線遅延モデルが用意されています。例えば合成中の回路規模が7000ゲートの場合は、その規模に合う配線遅延モデルを選択します。

配線遅延モデルを設定しないと配線の遅延時間が0になるため、タイミングの見積もりを正しく行うことが出来なくなります。したがって配線遅延モデルは必ず設定しましょう。(※一部のライブラリでは回路特性に応じて自動的に選択してくれる機能を持つものもあります。その場合はこのコマンドは不要です)

S2 図1:仮想モデルで配線遅延を見積もるときの考え方の図。ゲート遅延とゲート遅延の間に、配線長かける単位長あたりの遅延という配線分の遅延が入る。その配線分は、7000_GATE_MODELのような回路規模に基づく配線モデルから見積もる。
S2 図1:仮想モデルで配線遅延を見積もるときの考え方の図。横に長い図はスクロールして確認できます。

3. クロック(動作周波数)の設定 (create_clock)

論理合成ツールに対して合成中の回路の動作周波数(タイミング制約の基準)を与えるのが create_clock コマンドです。

# クロック周期と波形の設定(例: 50MHz, デューティ50%)
# -period (-p): 周期, -waveform (-w): 波形 {立ち上がり 立下り}
create_clock -period 20 -waveform {0 10} [get_ports CLK]

動作周波数50MHz(周期20ns)、デューティ50%のクロックを CLK ポートに対して設定する場合、上記のように記述します。最初の 0 は1周期の中の立ち上がり時刻、次の 10 は立下りの時刻を表します。この単位はライブラリに設定されているタイムユニット(例: ns)に依存します。

ここで与えられた周期がタイミング制約条件のベースとなり、フリップフロップ間の組み合わせ回路の遅延時間がこの周期よりも短くなるように最適化されます。

もふねこ

もふねこ:
制約条件(Constraints)って聞くと難しそうだけど、要するにツールに対する「このスピードで、この面積で作ってね!」っていうオーダーメイドの注文書みたいなものだよ🐾
特に create_clock は絶対に忘れないでね。これがないと、ツールがどのくらいのスピードで回路を走らせればいいか分からなくて迷子になっちゃうんだ!

設定したクロック波形 (CLK)

0 10 20 period 20 立ち上がり (0) 立ち下がり (10)

4. 回路面積の上限設定 (set_max_area)

論理合成ツールでは回路の規模を「面積」と表現します。回路面積の上限を設定するのが set_max_area コマンドです。回路の面積とは、回路中の「すべてのセルの面積」と「配線面積」の合計です。

set_max_area 1000

面積の上限を1000に設定する場合はこのようになります。

回路面積(Area)の概念

全セルの面積
+
配線面積
=
総面積 (上限: 1000)
タイミング制約と面積制約の優先順位

set_max_area が面積の制約条件で、create_clock 等がタイミングの制約条件になります。両方が設定されている場合、タイミングの制約条件が優先されます。つまり、論理合成ツールはまずタイミングを満たす回路を作り、そのあとで面積の制約条件を満たそうとします。

5. 入出力遅延の設定 (set_input_delay / set_output_delay)

入出力ポートに対してタイミング制約条件を設定するのがset_input_delayコマンドとset_output_delayコマンドです。

S2 図2:合成中の回路と外部環境の図。前段回路が入力ポートに入り、コア論理を通って出力ポートから後段回路へ出る。矢印にはinput_delayとoutput_delayと添えられている。
S2 図2:合成中の回路と外部環境の図。横に長い図はスクロールして確認できます。

set_input_delayコマンドで与えられた値は入力遅延といい、接続した回路のクロックの立ち上がりエッジに対する遅延値を表しています。
同様にset_output_delayコマンドで与えられた値は出力遅延といい、出力ポートに接続した回路の遅延を表しています。
実際には接続された回路の遅延量を推定し、適切な値を設定します。

遅延の設定方法を説明します。
まずそれぞれのコマンドの後に遅延値を書きます。
さらに基準となるクロック信号を-clock によって指定します。
そしてこれらの遅延値をすべての入出力ポートに対して設定します。
すべてのポートに同じ値を設定するとき、入力ポートの場合はall_inputs()、出力ポートの場合はall_outputs()とかきます。
正確には全てのポートの遅延は同じ値ではありません。個々のポートの値を全て見積もることは非常に手間がかかり、正確さにもかけるので一律に同じ値を設定しています。

set_input_delay 5 -clock CLK [all_inputs]
set_output_delay 5 -clock CLK [all_outputs]

入出力遅延のタイミング関係

CLK 入力信号 5ns (input_delay) 前段回路の遅延 ここから合成中の回路の処理に使える時間 出力信号 5ns (output_delay) 後段回路の遅延 ここまでに出力を確定させる必要がある

6. 入力信号の駆動能力設定 (set_driving_cell)

回路に入力される信号の駆動能力(ドライブ能力)を設定するのが set_driving_cell コマンドです。入力信号を駆動している前段のセル名を指定することで、ツールの遅延計算を正確にします。

# すべての入力ポートに対して、前段セルを 'BUF2' と仮定して駆動能力を設定
set_driving_cell -cell BUF2 -pin Z [all_inputs]

-cell でセル名を指定し、-pin でそのセルの出力ピン名(出力ピンが1つしかない場合は省略可)を指定します。駆動側の回路が合成済みでない限り正確な能力はわからないため、接続する回路の駆動能力が「小さい(ワーストケース)」と想定して小さなセルを設定するのが安全です。

これを設定しないと、入力の駆動能力が「無限大(遅延なし)」として扱われてしまい、実際のシリコン上でタイミングを満たさない原因になります。

S2 図3:合成中の回路と外部環境の図。BUF2 セル Z ピンが入力ポートを駆動し、入力ポートがコア論理に入る。
S2 図3:合成中の回路と外部環境の図。横に長い図はスクロールして確認できます。

7. 出力ポートの負荷容量設定 (set_load)

出力ポートには実際にどのようなセルが、いくつ接続されるのか事前に見積もる必要があります。
設定例としてすべての出力ポートに2入力のアンドセル3個に相当する負荷が接続されているとします。

まずset_loadコマンドの後に、load_ofを書いて括弧の中に論理合成用ライブラリ名、セル名、入力端子名をスラッシュで区切って書きます。
次にアスタリスクのあとに接続されているセルの個数を指定します。
そしてこれらの負荷容量を設定するポートを指定します。
ここではすべての出力ポートに同じ設定をしています。

set_load [expr 3 * [load_of my_lib/AND2/A]] [all_outputs]
S2 図4:外部環境と合成中の回路の図。コア論理から出力ポートへつながり、出力ポートに3個のAND2セルが負荷として接続される。
S2 図4:外部環境と合成中の回路の図。横に長い図はスクロールして確認できます。

8. ワンポイント・アドバイス

💡 面積の制約条件は常に「0」にする

面積の制約条件はつねに0にします。論理合成ツールは常にタイミングの制約条件を優先して論理合成します。タイミング制約を満たしたあと、面積の制約を満たそうとします。したがってタイミング制約を満たす範囲で最小の回路を得るために面積の制約条件は常に0にします。

💡 クロックラインは合成対象から外す

各ASICベンダーは配置、配線をするときにクロックラインを生成する独自の方法を持っています。この方法の方が性能がよくなります。
そこでクロックラインをASICベンダーまかせにするために論理合成時にはset_driveコマンドでクロックに無限大のドライブ能力を与え、set_dont_touch_networkコマンドでクロックポートにつながる配線を論理合成の対象から外します。
この設定をするとクロックラインに一切バッファがはいりません。

S2 図5:クロックポートの図。クロックポートが4個のフリップフロップに入る。矢印にはdont_touch_networkと添えられ、バッファ等の挿入や論理の変更を一切行わないと注記されている。
S2 図5:クロックポートの図。横に長い図はスクロールして確認できます。

実習で確かめる、つまずきの補足

制約とCDCを、機能テストとは別に確認する

50MHzの周期は20ns、100MHzは10nsだよ。クロック周期は希望する速さではなく、実際に入力するクロックと設計要件に合わせる。

# clk_inが50MHzの設計例。ポート名は実際のトップと合わせる。
create_clock -name sys_clk -period 20.0 [get_ports clk_in]

クロックを速くするということは、周期を短くするということだよ。同じ回路でも、FFからFFまでの経路を通る時間は変わらないから、周期を縮めていけばいつか間に合わなくなる。周波数を上げたときに動かなくなる原因の一つだね。セットアップとホールドの条件そのものはL3の補足にあるよ。どの経路がどれだけ足りないかは、合成後のタイミング解析で読むんだ。

これだけで全制約が完成するわけではない。外部デバイスとの入力/出力遅延、生成クロック、クロック間の関係、例外の根拠も確認するよ。Slackだけでなく未制約パスと制約対象の件数を読む。クロックを遅くすればsetupが改善する場合はあるけれど、同一クロックのhold違反はそれだけでは解消しないんだ。

非同期の1ビットレベルを別クロックへ渡すときは、受信側の2段以上の同期FFを用いてメタスタビリティが後段へ伝わる確率を下げる。確率をゼロにするものではなく、短いパルスを必ず捕まえる保証もないよ。パルスはハンドシェイクやトグル方式、複数ビットのまとまりはハンドシェイクや非同期FIFOなど、データの性質に合う方式が必要だね。

メタスタビリティはFFの出力が安定した値へ確定するまで長くかかる物理現象で、RTLの記号Xそのものではないよ。同期段数だけで安全を保証せず、クロック周波数・入力の変化頻度・解決時間を含むMTBFを確認しよう。Intelのメタスタビリティ解析の説明にも、同期チェーンとMTBFの関係が整理されているよ。

非同期リセットは、解除がクロックに近いとrecovery/removalの条件に関わる。実機ではクロックドメインごとの解除同期も設計しよう。TBで負エッジに解除するのはシミュレーションの競合を避ける工夫であり、実機用のリセット同期回路の代わりにはならないよ。