01signal.com

multi-cycle paths用Timing constraints

このページは、 timingに関する一連のページに属しています。前のページでは、 timing 計算の背後にある理論を説明し、いくつかの timing constraints を記述する方法を示し、 timing closureの原理について説明しました。このページでは、multi-cycle pathsの timing constraints について説明します

序章

multi-cycle paths について最初に知っておくべきことは、通常は悪い考えだということです。このページでは multi-cycle path constraintsの使用方法を説明していますが、結論としては、この手法を完全に回避する必要があります。 ASIC の世界でこの手法を使用する理由はいくつかありますが、 FPGA design では通常、代わりに clock を追加する方が適切です。

とはいえ、この種の timing exception がいつ関連するか見てみましょう。

次の Verilog コードの例を考えてみましょう。

reg foo, bar;
   reg en, pre_en;

   always @(posedge clk)
     begin
	pre_en <= !pre_en;
	en <= pre_en;

	if (en)
	  begin
	     foo <= !foo;
	     bar <= foo;
	  end
     end

この例は不完全であることに注意してください。 おそらく、 synthesis attributes を @pre_en と @enに追加する必要があります。そうしないと、 synthesizerの最適化によって予期しないことが起こる可能性があります。詳細については、以下をご覧ください。

@pre_en は、 clock cycleごとに '0' と '1' の間を行き来します。 @en は、少し遅れて同じことを行います。

"if (en)" の部分は、"begin" と "end" の間のすべてに対して @en が clock enable としての役割を果たすことを意味します。 @en が Low の場合、 Verilog コードのこの部分では何も起こりません。つまり、 @en が Low の場合、 @foo と @bar は clock edge が存在しないかのように動作します。

この例では、 @en は 2 つの clock cyclesごとに 1 回高くなります。したがって、 @foo と @bar は、 clockの周波数が実際の周波数の半分であるかのように動作します。したがって、 timing の要件はそれに応じて緩和できます。 tsetup の計算は、2 倍の大きさの clock period で実行できます。

tholdに関しては、変更はありません。 この timing 要件の計算では、同じ clock edge が両方の flip-flopsに到達することを前提としています。したがって、 thold 分析の例ですでに説明したように、 clock period には意味がありません。したがって、 clock が遅いという錯覚は、 tholdに関しては何の違いもありません。

clock enableを使用する場合

clock enableを使用する正当な理由は 2 つだけです。

clock enable が FPGAの消費電力を改善するかどうかは明らかではありません。おそらく、余分な clock は電力を浪費します。しかし、 clock enable は fan-outの高い signal でもあります。平均して、 clock enable は、代用する clock と同じくらい頻繁に値を変更します。したがって、 logic state (消費電力の主な要因) の変更に関しては、違いはありません。

multi-cycle pathsを使用しなければならない特別な理由がない限り、このページは読み飛ばしていただいてかまいません。技術的に適した design に clock enable がある場合でも、該当する timing exceptionを使用しない方がよい場合があります。特に、この timing exceptionなしで簡単に timing constraints を達成できるのであれば、失敗するリスクを負う価値はありません。

timing exception

上記の Verilog コードに関連して、これらは Vivado用の multi-cycle path constraints です。

set en_regs [all_fanout -endpoints_only -only_cells -flat [get_nets en]]
set_multicycle_path -setup -from $en_regs -to $en_regs 2
set_multicycle_path -hold -from $en_regs -to $en_regs 1

Quartusについても同様です。

set en_regs [get_fanouts en]
set_multicycle_path -setup -from $en_regs -to $en_regs 2
set_multicycle_path -hold -from $en_regs -to $en_regs 1

Vivado と Quartus の違いは最初の行だけです。 コマンド「all_fanout」は Vivadoで使用され、「get_fanouts」は Quartusで使用されます。 constraints は、 SDCで動作する他のツールと同様です。

最初の行は、 @enで始まる任意の path の終わりであるすべての synchronous elements (cells) を検索します。この cell objects のリストは $en_regsに保存されます。次の数行は、このリストにある cells で開始および終了する paths の timing 要件を変更します。

これには多くの説明が必要です。 $en_regs の定義をこのように書いたのはなぜですか? set_multicycle_path コマンドが 2 つあるのはなぜですか? thold の timing 要件は multi-cycle pathsの影響を受けないと言いましたが、2 番目のコマンドで「-hold」と表示されるのはなぜですか?

set_multicycle_path コマンドから始めましょう。そのほうが説明しやすいからです。

set_multicycle_path コマンド

上記のように、コマンドは次のとおりです。

set_multicycle_path -setup -from $en_regs -to $en_regs 2
set_multicycle_path -hold -from $en_regs -to $en_regs 1

上記の例をある程度一般化するために、これも考えてみましょう。 clock enable が 8 つの clock cyclesごとに 1 回アクティブだった場合、 Verilog code は次のようになります。

reg en;
   reg [2:0] pre_en;

   always @(posedge clk)
     begin
	pre_en <= pre_en + 1;
	en <= (pre_en == 0);
     end

@en は strobeのように動作し、毎回 1 つの clock cycle の間だけアクティブになることに注意してください。 clock dividerの MSb ではありません。

この可能性のある multi-cycle constraints は次のようになります。

set_multicycle_path -setup -from $en_regs -to $en_regs 8
set_multicycle_path -hold -from $en_regs -to $en_regs 7

これら 2 つの例を考えると、最初の set_multicycle_path コマンドの数値が単純に clock enableの分周比であることは明らかです。

2 番目のコマンドについては、同じ分周比ですが、マイナス 1 です。したがって、常に N と N-1です。

最初のコマンドに関して説明することはあまりありません。 Nのうちの 1 つの clock cycle で clock enable がアクティブな場合、許可された delay は Nで乗算されます。これは、 tsetupの timing 要件に関連しています。

しかし、なぜ 2 番目のコマンドがあるのでしょうか。 tholdについて何か言う必要があるのはなぜですか?答えは、最初のコマンドが最小の delayの要件も変更することです。つまり、 thold の計算も、"-setup" オプションを指定したコマンドの影響を受けます。なぜ?おそらく良い説明はありません。

2 番目のコマンドはこれを修正します。 最小 delay の要件を元の値に戻します。したがって、2 番目のコマンドの後、 thold の計算は以前と同じように行われます。

N-1 が 2 番目のコマンドで使用される理由について、ドキュメントに長い説明があります。しかし、正直なところ、その余分な情報には何も興味深いものはありません。 thold に関する set_multicycle_path の動作は奇妙であり、数値が N-1 であるべき理由を理解しても、それほど奇妙ではありません。

しかし、 set_multicycle_path は簡単な部分でした。ここからが本当の難しさです。 $en_regsとして使用するための cell objects の正しいリストを生成します。

registersの選択

どの registers を $en_regsにリストするかを選択するには、 path が multi-cycle pathとして適格である理由を理解する必要があります。したがって、これがルールです。 clock enable が両側を制御する場合にのみ、 path の timing 要件を緩和することができます。これは、 clock enable が非アクティブの場合、2 つの sequential elements のどちらも clock edgeの後にその値を変更しないことが保証されることを意味します。

clock domainsの観点から考えてみましょう: clock enable によって制御されるすべての sequential elements は、架空の clock domainに属します。この架空の clock domain 内の clock は周波数が低いため、この clock domain 内の timing 要件を調整できます。

しかし、 pathの側面の 1 つがこの架空の clock domainに属していない場合、これは 2 つのrelated clocks間の架空の clock domain crossing です。このような pathについて特別なことをする必要はありません。既存の timing constraintsによって既に処理されているからです。しかし、この種の path に multi-cycle exception を適用するのは正しくありません。

上記の timing constraints は、この考えを反映しています。 @en によって制御されるすべての sequential elements は、 cell objectsとして $en_regs にリストされます。次に、このリストに属する sequential elements で開始および終了する 2 つの set_multicycle_path コマンドが paths に適用されます。

multi-cycle paths に関する最も難しい部分は、この sequential elements のリストが正しいことを確認することです。 このリストには、 clock enableによって制御されるすべての sequential elements が含まれている必要があります。しかし、他の sequential elements をこのリストに載せるべきではありません。

sequential element がこのリストにない場合、関連する paths に対する timing の施行は必要以上に厳しくなります。これは大惨事ではありませんが、 timing exception の効率を低下させます。

ただし、リストにないはずの sequential element が誤ってリストに追加された場合、重大な結果が生じる可能性があります。 これにより、 timing の計算が不十分な paths が生成されます。つまり、ツールは sequential elementsの適切な動作要件を保証するものではありません。そして、 timing の要件が満たされない場合、奇妙なことが起こる可能性があります

この sequential elementsのリストを作成するために、"all_fanout" (または "get_fanouts") コマンドを使用することにしました。これは、常に正しく動作することが保証されているわけではありません。これについては、次に説明します。その後、このリストを作成するための他のオプションについて説明します。これらの他のオプションは、特に FPGA ツールが「all_fanout」または同様のコマンドをサポートしていない場合に関連します。

all_fanout および get_fanoutsで起こりうる問題

multi-cycle constraint で最もありそうな間違いは、 clock enable 自体 (つまり @en) がリスト (つまり $en_regs) に含まれていることです。この場合、 multi-cycle exception は、 @en 自体からそれが制御する sequential elements までのすべての paths に適用されます。これは事実上、これらの paths の timing 要件が保証されていないことを意味します。 clock enable は多くの場合、 fan-outが高い signal であるため、これは目に見える影響を与える可能性があります。

上記の例では、これは @pre_enで回避されます。 @en が次のように定義されていない理由を自問したかもしれません。

always @(posedge clk)
  en <= !en; // Wrong!

@en がこのように定義されていた場合、 @en からそれ自体への path が存在することになります。その結果、 @en は $en_regsに含まれていたはずです。

したがって、 @pre_en はこの問題を解決します。ただし、最適化のために synthesizer がこの register を排除しないようにすることが重要です。たとえば、 Quartusの synthesizer は、 @pre_en の唯一の用途が @en に値を与えることであることを検出します (このページの上部にある Verilog コードで)。したがって、 synthesizer は @pre_enを削除し、「en <= !en」と言うように続行します。その結果、 @en は $en_regsに含まれます。可能な解決策は、次のように @pre_en を宣言することです。

reg pre_en /* synthesis preserve */;

この単純な例は、 synthesizer による予想外の最適化が悲惨な結果をもたらす可能性があることを示しています。ソリューションは単純ですが、この最適化を回避する必要性を見落としがちです。

clock enable で考えられるもう 1 つの事故は、この signal の fan-outが高いことが多いという事実に関連しています。したがって、ツールは register を自動的にレプリケートし、各レプリカの fan-out が特定の制限よりも低くなるようにします。しかし、これは $en_regsにどのような影響を与えるでしょうか?このリストに含める基準は、特定の netに基づいています。したがって、 @en のレプリカによって制御される sequential elements は含まれません。

ただし、このような状況の結果は悲惨ではありません。 すでに述べたように、これは一部の paths に対する timing の施行が必要以上に厳しくなることを意味するだけです。 designの信頼性は影響を受けません。

registers のレプリケーションについては、 fan-outsが高い場合のコンテキストで既に説明しました。そこで議論されているように、 synthesizer が複製を行うのを待つよりも、 @en を手動で複製する方がよいでしょう。予期せぬ事態を避けるために、この registerの複製を許可しない synthesis attribute を常に追加してください。高い fan-out が後で timing closure に問題を引き起こす場合は、手動で複製して解決してください。この方法で問題の原因を理解しやすくなります。プロジェクトが成長したために synthesizer が突然 @en を複製した場合、 timing constraints が達成されなかった理由を理解するのはそれほど簡単ではありません。

いずれにせよ、 @en のレプリカが含まれるように、 $en_regs を定義するコマンドを更新する必要があります。

そういえば、 $en_regs の定義は netの名前に依存していることに注意してください。すでに説明したように、これは、 synthesizer が net の名前を「en」とは異なる名前に変更した場合、 $en_regs が空のリストになることを意味します。その結果、 multi-cycle path constraints は完全に役に立たなくなります。この可能性も災害ではありません。 design は依然として信頼性がありますが、 timing constraintsを実現するのはより困難です。

もう 1 つの考えられる問題は、 $en_regs の定義方法が原因で、 @en を clock enable以外の目的で使用してはならないことです。たとえば、次の Verilog コードを考えてみましょう。

reg [7:0] counter;

always @(posedge clk)
  if (en)
    counter <= counter + 1;

この例では、 @en は明らかに clock enableとして使用されています。したがって、 @counter に関連するすべての paths が multi-cycle pathsであっても問題ありません。しかし、これはどうですか?

reg [7:0] counter;

always @(posedge clk)
  if (en)
    counter <= counter + 1;
  else
    counter <= counter - 1;

ここで、 @en は他の registerと同じように使用されます。 @counter の値は、 clock cycleごとに変わります。したがって、 @counter が multi-cycle pathの候補になることは絶対にありません。それでも、 @counter のすべての flip-flops は $en_regsに含まれています。 これらすべての flip-flopsに @en から paths があります。

これは、 @enのレプリカを作成することで、比較的簡単に解決できます。

reg [7:0] counter;
reg non_ce_en;

always @(posedge clk)
  non_ce_en <= pre_en;

always @(posedge clk)
  if (non_ce_en)
    counter <= counter + 1;
  else
    counter <= counter + 2;

synthesizer が @en と @non_ce_en を 1 つの registerにマージするのを防ぐには、 synthesis attribute が必要であることに注意してください。

結論として、 @en で始まるすべての paths に基づく $en_regs の定義は単純で簡潔です。しかし、この定義は地雷原でもあります。それでは、いくつかの代替案を見てみましょう。

$en_regsを作成する別の方法

multi-cycle path timing exception の sequential elements のリストを作成する最も安全な方法は、 design hierarchyに依存することです。 clock enable によって制御されるすべての logic は、別の module (および場合によっては sub-modules) にある必要があります。これにより、 cell objectの完全な名前に基づいて cells を検索することにより、 $en_regs を作成できます。たとえば、 Vivadoの場合:

set all_sync [all_fanout -endpoints_only -only_cells -flat \
  [get_nets -of_objects [get_clocks clk]]]
set en_regs [filter $all_sync {name =~ module_ins/multicycle_ins/* }]

最初のコマンドは、 clock buffer 自体を除く、 @clk に接続されているすべての logic elements を検出します (この clock は、「clk」という名前の clock object で表されます)。結果は $all_syncに格納されます。これは、関連する可能性のあるすべての synchronous elements を含むリストを作成する方法の 1 つです。 2 番目のコマンドは、前述の別の module内にある $all_sync 内のすべての logic elements のリストを作成します。

このメソッドは、 clock object の名前と instantiationsの名前に依存していることに注意してください。これらは変更される予定はありません。この方法では、 clock enable が複製されるかどうか、またはその名前が synthesizerによって変更されるかどうかは関係ありません。

別の module のもう 1 つの利点は、 Verilog コードでの作業が容易になることです。 clock enable によって制御される sequential elements とそうでない sequential elements の間で混乱が生じる可能性はほとんどありません。

ただし、 clock enableに依存する logic を分離して、別の moduleに入れることは必ずしも自然ではありません。さらに、 Verilog コードが既に作成されていて、正しく動作することがわかっている場合は、それに変更を加えることはお勧めできません。

また、状況によっては適切な別の代替手段についても言及します。 すべての registersの命名規則。たとえば、 clock enable によって制御されているすべての registers に「MC_」で始まる名前を付けることができます。この選択により、 $en_regs を作成するためのコマンドが簡単になります。 名前に従って cells objects を検索するだけです。他の logic elements (例えば block RAMs) も、それに応じて instantiationの名前を選択することで含めることができます。この方法は Verilog コードを醜くすると言う人もいれば、扱いやすくすると言う人もいます。完璧な方法はありません。

-of_objectsを使用しない理由

単純な基準に従って $en_regs を定義するのは魅力的に思えるかもしれません。 「en」という名前の net を見つけ、この netに接続されているすべての registers を追加します。たとえば、 Vivado の場合は次のように記述できます。

set en_regs [get_cells -of_objects [get_nets en]]

これが間違っている理由はいくつかあります。 1 つ目の理由は、これには @en 自体が含まれているためです。したがって、 multi-cycle constraints は、 clock enable 自体からそれが制御する sequential elements までのすべての paths に適用されます。前述のとおり、これは重大な誤りです。

2 つ目の理由は、一部の sequential elements が見落とされている可能性があることです。上記のコマンドによると、含める基準は、 cell を特定の net (@en) に接続する必要があることです。これは、この net が flip-flopの CE inputに直接接続されている場合に機能します。しかし、多くの場合、 synthesizer は代わりに @en に基づく combinatorial function を使用することを選択します。

たとえば、 synthesizer は、 Verilog コードが次のようであるかのように、 @foo を実装することを選択できます。

foo <= foo ^ en;

これは元の式と機能的に同等です。

if (en)
  foo <= !foo;

この種の最適化は正当であり、期待する必要があります。 synthesizer は、 NOT 機能を実装するために、とにかく LUT を使用する必要があります。では、この LUT を使用して、 flip-flop の next value を直接取得してみませんか? flip-flopの CE inputに別のワイヤを追加する理由は?

この最適化の副作用は、 flip-flop 自体が @enに直接接続されないことです。したがって、 $en_regsには含まれません。このような状況がもたらす影響については、すでに議論されています。

synthesizer に @en を synchronous elementsの clock enable inputとしてのみ使用するように指示することは、しばしば可能です。たとえば、一部の synthesizers は、「direct_enable」などと呼ばれる synthesis attribute をサポートしています。この機能を使用すると、 synthesizerが logic を最適化する自由度が低下することに注意してください。したがって、 designのパフォーマンスは、ツールの技術的な問題を解決するために悪影響を受ける可能性があります。

さらに、 @en を複製したり、名前を変更したりすると、上記と同じ問題が発生します。

したがって、これらすべての理由から、 net への直接接続を基準として使用するのは適切ではありません。

resetとの相互作用

上記の Verilog の例に synchronous reset を追加するとします。

always @(posedge clk)
     begin
	pre_en <= !pre_en;
	en <= pre_en;
     end

   always @(posedge clk)
     if (reset)
       begin
	  foo <= 0;
	  bar <= 0;
       end
     else if (en)
       begin
	  foo <= !foo;
	  bar <= foo;
       end

multi-cycle timing exception の背後にある考え方は、すべての synchronous elements が架空の clock domainの一部であるかのように動作するというものであったことを思い出してください。この clock domain 内の架空の clock は、 @clkの半分の周波数を持っています。したがって、 @en が Low の場合、すべての registers は @clk を無視する必要があります。これは、次の Verilog コードの最後の例には当てはまりません。 @reset は @enに関わらず効果があります。

たとえば、 @reset が次のように定義されているとどうなるかを考えてみましょう。

assign reset = foo;

これは正当な synchronous resetですが、実際にはほとんど使用されていません。しかし、この定義は multipath exceptionの問題を示しています。 @en が High になると、その後 clock cycle で @foo が High になります。しかし、それは @reset も高くします。したがって、次の clock cycleでは、 @foo が再び低くなります。 @foo は clock cycleごとに値を変更します。したがって、 @foo からそれ自体への multicycle path は、この pathでは十分に厳しくない timing 要件を引き起こします。

これは、 @en が synchronous reset も制御するようにすることで簡単に解決できます。

always @(posedge clk)
     if (en && reset)
       begin
	  foo <= 0;
	  bar <= 0;
       end
     else if (en)
       begin
	  foo <= !foo;
	  bar <= foo;
       end

これは正しいですが、 @reset は @enと共にアクティブにする必要があります。簡単な解決策は、いくつかの clock cyclesに対して @reset を高く保持することです。

asynchronous resetにも同じ原則が適用されます。 asynchronous resetsに関するページに書かれていることはすべてここでも関連していますが、 multi-cycle path constraintsではさらに複雑です。最も簡単な解決策は、別のページで提案されているように、おそらく synchronizerを使用することです。

@en および asynchronous resets

@pre_en の利点の 1 つは、 @en のすべてのレプリカが常に同じ logic level を持つことを保証することです。これは当たり前のように聞こえるかもしれませんが、 asynchronous reset が誤って使用された場合は保証されません。たとえば、元の Verilog コードが次のとおりであるとします。

reg en;

always @(posedge clk or posedge reset)
  if (reset)
    en <= 0;
  else
    en <= !en; // This is not recommended!

@en がレプリケートされる場合、結果は次のようになります。

reg en, en_1, en_2;

always @(posedge clk or posedge reset)
  if (reset)
    begin
      en <= 0;
      en_1 <= 0;
      en_2 <= 0;
    end
  else
    begin
      en <= !en;
      en_1 <= !en_1;
      en_2 <= !en_2;
    end

@en_1 の next value は、 @enの値ではなく、それ自体の現在の値に依存することに注意してください。これは、 registerの複製の現実的な結果です。

asynchronous reset が安全でない方法で非アクティブ化された場合はどうなりますか? @en の一部の複製が resetの後の最初の clock edge に反応し、他の複製がこの clock edgeを無視する可能性があります。その結果、レプリカの logic state が同じになることはありません (次の resetまで)。

すべてのレプリカが同じソースから next value をコピーするため、@pre_en はこれを解決します。これにより、ラフなスタートがあっても、長期的には適切な動作が保証されます。

概要

clock enableの使い方は簡単です。 set_multicycle_path を timing exceptionのコマンドとして使用するのは簡単です。しかし、これを確実に機能させるのは簡単ではありません。うまくいかないことはたくさんありますが、 logic designの成長に応じて synthesizer の動作が変化することが原因である場合もあります。

これらの予期しない問題の結果、 multi-cycle path がその目的を果たせなくなる可能性があります。 緩和された timing 要件が一部の pathsに適用されない場合、この方法の利点は疑わしいものになります。さらに悪いことに、影響を受けるべきではない paths に multi-cycle path exception が適用された場合、ミスによって logic designの信頼性が低下する可能性があります。

したがって、予期しない事態が発生していないことを確認するために、関連する paths の timing reports を読み取ることが重要です。残念ながら、これは将来の驚きを防ぐものではありません: design が進化するにつれて、 synthesizerの動作を予測することは困難です。

したがって、可能であれば、 clock enableを使用する代わりに、同じ PLL から追加の clock を生成することをお勧めします。 2 つの clocks がrelated clocksであるため、この新しい clock を備えた clock domain crossings は信頼できます。この新しい clock の timing constraints は、ツールによって自動的に生成されます。この方法では、驚きのリスクはありません。

designに clock enable と multi-cycle exception を追加したいという理由でこれを読んでいるのであれば、このページで何か考えるきっかけになれば幸いです。


これで、 FPGAの内部にある paths の timing constraints に関する部分は終了です。しかし、 I/Oはどうでしょうか。それが、次のページでの議論の始まりです。

このページは英語から自動翻訳されています。 不明な点は元のページを参照してください。
Copyright © 2021-2024. All rights reserved. (6f913017)