01signal.com

FPGA上のResets : 同期、非同期、またはまったくない?

これは、 FPGAsの resetsに関するシリーズの 2 ページ目です。 asynchronous resets が前のページで多くの人が考えるものではない理由を説明した後、このページでは resets のさまざまなオプションとFPGAの初期化について説明します

何よりもまず: resetとは?

たとえば、 reset が何であるかは、誰でも知っています。コンピューターで button を押すと、すべてがクリーンな状態から始まるようなものです。 chip に送られるその信号によって、以前に何が起こったとしても、これからはすべてが正常になります。 reset は、システムを既知の状態にする信号であると考える人もいます。

FPGA designersにとって、 reset は、すべての module に追加して繰り返しコード パターンで使用するための追加の input に過ぎないことがよくあります。これは、この reset signal が実際に何を保証するのか、あるいはまったく保証されないのか、あるいは完全に省略できるのかに必ずしも注意を払わずに、単に行うものです。

最も一般的な誤解は、 reset signal がシステムを既知の状態にするという事実に焦点を当てていることです。もちろんそれは事実ですが、重要なのは reset が非アクティブ化された後に何が起こるかです。 logic は予測可能な方法で動作を開始するようにする必要があります。少なくとも、適切に動作することを保証できるほど予測可能でなければなりません。 resetを非アクティブ化した後の動作が不明な場合、システムをリセットしても意味がありません。

このトピックを特に難しくしているのは、運が関係していることです。 一般的に言えば、 reset がアクティブになったときのシステムの状態は不明でランダムであり、 resetが非アクティブになるタイミングも同様です。したがって、 reset signal を不適切に扱うと、ランダムな機会にまれに発生する誤動作が発生する可能性があります。これは、まったく異なる種類の問題であるように見える場合があります。同様に、通常魔法で対処される偶発的な問題を除き、この問題を目に見える影響なしに無視することも可能です。

適切な timing constraintsと同様に、 clocks と clock domainsの適切な処理、 FPGAのウェイクアップとリセットの適切な処理は、 FPGA が確実に動作することを保証するために必要です。これらすべてのトピックに共通するのは、それらを無視することである程度回避できるということです。かなりの数のエンジニアがそうしていますが、残念ながら、 FPGA が時々取り憑かれているように動作するという犠牲が伴います。

シミュレーションとハードウェア

このページでは、 resets に関する決定が、 FPGAにロードされたときに design にどのように影響するかに焦点を当てています。しかし、明らかに、これらの決定は logicのシミュレーションにも影響を与えます。

これら 2 つの状況を区別することが重要です。シミュレーターは、すべての registers、特に behavioral simulationに X (不明) 初期値を割り当てます。これらの X 値は、 logic functionの X 値に依存するすべての register に伝播します。そのため、 X 値を持つ 1 つの register でさえ、 X'sを持つ design 全体を圧倒し、 simulation が役に立たなくなることがあります。

この問題に対するよくある誤った解決方法は、 designのすべての registers に asynchronous reset を配置して、すべての X'sを削除することです。これは通常、 simulationの開始時に reset を一時的に有効にすることによって行われます。その結果、すべての registers が既知の値を取得し、すべてが完璧に見えます。残念ながら、この誤った解決方法は、前のページで説明した問題を隠して、クリーンな開始の錯覚を与えることがよくあります。

resets が正しく使用されている場合でも、シミュレーションで X 値のソースを追跡することを避けるために、すべての registers をリセットするのは怠惰な選択です。これはリソースを浪費するだけでなく、 timing constraintsの実現を難しくする可能性があります。 registers を不必要にリセットすると、バグが隠れる可能性もあります。これは、 X 値のフラッドが、ある register から別の register への意図しない依存関係に起因する可能性があるためです。したがって、この X's のフラッドは、 designに何か問題があるという警告である可能性があります。

simulationと比較すると、ハードウェアは resetsを使用しないことに対してはるかに寛容です。しかし、 resets が誤って使用されたり、必要なときにまったく使用されなかったりすると、ハードウェアがまったく予期しない動作をする可能性があります。

結論として、 resets はハードウェアを念頭に置いて使用すべきであり、 simulation中に煩わしい X's を排除するために使用すべきではありません。また、ハードウェアに重点を置く必要があるため、 simulationsについて私が言うべきことはこれだけです。

リセットの戦略

registers に resets を適用するかどうか、またどのように適用するかを決定するには、各 register のケースを個別に検討する必要があります。これを行うと、 reset signalsの fan-out が不必要に高くなるのを回避できるだけでなく、以前の状態に関係なく、 resetの後に logic が正しく開始されることが保証されているかどうかを考える良い機会にもなります。

原則として、次の 4 つのオプションがあります。

私の意見

これらの各オプションについては以下で説明します。そのため、最初に正しいと思われる方法を提示し、次に詳しく説明します。

これらの提案は、最近の FPGA ベンダーの推奨事項とほぼ一致しています。

非常に一般的なガイドラインも追加します。 常に control logic を明示的にリセットし、 data paths が初期の junk dataから自分自身をフラッシュできるようにします。

そして今、各オプションについての長い議論。

オプション1: 初期値不明

一部の registers では、 reset も初期値も必要ありません。これは特に shift registers および同様の logic elementsに当てはまります。一般的に言えば、 data paths はこのオプションに適合する可能性があります。

次のスニペットを検討してください。

reg [31:0] d0, d1, d2, d3, d4;

always @(posedge clk)
  begin
    d4 <= d3;
    d3 <= d2;
    d2 <= d1;
    d1 <= d0;
    d0 <= orig_data;
  end

これは明らかに 5 つの delay registers とそれぞれ 32 bits です。一部の FPGAs (特にXilinx ) では、最後の値 (@d4) のみが使用され、これらの registersのいずれにも reset がない場合、 synthesizer はこれを shift register として検出します。これにより、 logic の消費を大幅に削減できます。

明らかに、これらの delay registers の output に接続されている logic は、いくつかの初期ランダム データを許容できなければなりません。これが問題にならない典型的なケースは、他の register または state machineがあり、適切にリセットされ、初期化されていない値が到着しても無視されるようになっている場合です。たとえば、これらの delay lines が pipelineに関連している場合、 pipelineの control logic は当然無効なデータを無視します。

もっと一般的に言えば、 register をリセットも初期化もしない機会は、その有効性を示す付随フラグまたは state がある場合に簡単に認識できます。または、最初に register に値を割り当て、次にこの値を使用するという明確なシーケンスがある場合です。つまり、適切な値が割り当てられるまで registerの値は無視されることが簡単にわかる場合です。

synthesizerに依存している可能性がある場合は、別のタイプの未知の初期値です。例えば:

reg val;

always @(posedge clk)
  val <= 1;

synthesizer は、 @val が 1の定数値を持つ wire であると判断する場合があります。もう 1 つの可能性は、 register に初期値としてゼロを割り当てることです。これにより、最初の clockで 1 に変更されます。実際に何が起こるかは、 synthesizerによって異なります。したがって、最初の clock cycleを除いて、 @val の値は常にわかっていますが、初期値は不明であると見なす必要があります。

オプション #2: FPGAの初期値

FPGA (一般的に flip-flops、 shift registers とメモリ)の基本 synchronous elements の初期値は、 configuration bitstreamで与えられます。この機能のよく知られた使用法は、 block RAM に初期値を割り当てて ROM を作成し、それに書き込みを行わないことです。

この機能のもう 1 つのよく知られている側面は、 FPGA は通常、明らかにすべての registers の値が 0 である configuration からウェイクアップすることです。これは、 synthesizer が通常すべての registers に初期値として 0 を割り当てるためですが、初期値が明示的に設定されていない限り、予期せぬ事態が発生する可能性があります。

一部の synchronous elements、特に shift registers および専用の RAM blocksは、 Verilog または VHDL で動作を記述することによって作成できます ( inference): synthesizer は通常、コードが delay lineのように見える場合に shift register を作成します。同様に、 arrayに応答して RAM logic element が作成されます。ただし、これらの registers (synchronous reset または asynchronous reset) で reset が使用されている場合、 synthesizer はこの方法で logic リソースを使用できません。これは、 shift registers にも RAMs にも、内部メモリの値を設定する reset input がないためです。

では、初期値はどのように設定されているのでしょうか? configuration プロセス中、 FPGA がアクティブになる直前、つまり synchronous elements が clocks および asynchronous reset inputsへの応答を開始する前に、すべての synchronous elements に初期値が与えられます。 Xilinx デバイスの場合、これは、すべての synchronous elements を初期状態にする Global Set Reset (GSR) シグナルによって実装されます。この後、 Global Write Enable (GWE) がアクティブになり、 synchronous elements が正常に動作します。

configuration プロセスは、 FPGAの application logicによって使用される clocks のいずれにも関係なく実行されるため、 FPGAの動作状態への移行は、これらの clocksのいずれにも非同期です。その結果、 synchronous elements は、 clockに関係なく、 asynchronous reset が非アクティブ化された場合とまったく同じように動作します。言い換えると、一部の synchronous elements は動作状態への移行後に到着する最初の clock edge に応答し、他の synchronous elements は timing違反のためこの clock edge に応答しない可能性があります。このシリーズの最初のページで説明したように、これは厄介なバグを引き起こす可能性があります。

FPGA が起動したときに clocks が必ずしも安定しているとは限らないことに注意することが重要です。 FPGA自体の PLLsによって生成された場合、 timing constraints に大きく違反する可能性があります。上で説明したように、 clock が安定するまで (たとえば、 clock enableによって) 無視される場合、または FPGA が起動したときに安定していることがわかっている場合、これは問題ではありません。また、 clock が安定するまで、 synchronous element がその値を変更する理由がないことを design が保証する場合、問題ありません。そうでなければ、初期値を設定してもあまり保証されません。

初期値の設定には制限がありますが、多くのシナリオではこれで十分であり、明示的な reset は必要ありません。また、 reset signal が利用できないために選択の余地がない場合もあります。たとえば、 FPGA が起動した直後に FPGAの reset signals を作成する logic などです。このような logic の例は、このシリーズの 3 ページ目に示されています。

ほとんどの synthesizersでは、 registerの初期値の設定は非常に簡単です。 Verilogの "initial" を使用:

reg [15:0] counter;
initial counter = 1000;

「initial」が synthesisの Verilog コードで使用できることは驚くべきことかもしれませんが、実際のところ、この使用法は広くサポートされています。したがって、 synthesizer がサポートしている場合は、この keyword が間違いなく推奨される方法です (つまり、「initial」のこの使用法はドキュメントに明示的に記載されています)。さらに、代替方法はベンダー固有である傾向があり、場合によっては FPGA ファミリに固有のものさえあります。したがって、「initial」が常に持ち運び可能であるとは限りませんが、おそらく最も持ち運びやすい選択肢であることに変わりはありません。

初期値を設定する代替方法は、使用する FPGA によって異なります。この方法は通常、 synchronous element の instantiation を primitiveとして、初期値を instantiation parameterとして代入することで構成されます。たとえば、 Xilinxの flip-flop:

FDCE myflipflop (
  .C(clk),
  .D(in),
  .Q(out),
  .CLR(1'b0),
  .CE(1'b1)
);

defparam myflipflop.INIT = 1;

「initial」の方がいいですよね。

オプション #3: Asynchronous reset

asynchronous resets がしばしば間違って使用される理由に関するページをまだ読んでいない場合は、最初に読むことをお勧めします。とにかくこの種の reset を使用するつもりがない場合を除きます。

何らかの理由で、多くの人が asynchronous reset をあらゆる目的に適したソリューションと考えています。コード例によく登場するためか、すべての synchronous elementsに到達する global reset を実現する簡単な方法の幻想のためかもしれません。おそらく、古い ASIC の世界では、 asynchronous reset は chip 全体をリセットして test vectorsの適用を開始できるため、製造プロセスで chip test に役立ちました。

だから現実に: asynchronous reset を使用する適切でクリーンな方法は、 clocks をオフにして使用することです。それが「asynchronous」の部分の本当の意味です。実際の designでは、これは次の段階で構成されます。

このシーケンスの実装は難しくありませんが、各 clock の最初の edge が適切に形成されていることを確認するのは難しい場合があるため、 glitchesはありません。 clock buffers の一般的な問題は、 clock bufferの output enable のアクティブ化と、 clock bufferを通過する最初の clock edge との間のタイミングに関する要件があることです。この timing requirement に違反すると、 clock buffer は glitch ( clockに対する FPGAの要件に違反する短い pulse ) を出力する可能性があります。これにより、この clockに依存するすべての synchronous elements の予期しない動作が発生する可能性があります。

残念ながら、 FPGA ベンダーが提供するドキュメントには、この timing requirementを確実に動作させる方法が必ずしも説明されているわけではありません。その結果、 reset の後の最初の clock edge が正しく動作することを保証できない可能性があります。また、最初の clock edgeが保証されなければ、 reset は無意味です。

この方法を使用する場合は、 asynchronous resetに関連する paths に timing constraints が適用されていないことを確認してください。この場合、そのような強制は不要であり、デフォルトで有効になっている場合があります。

asynchronous reset を確実に適用するための代替方法があり、一般的に提案されています。この方法は clock gatingを含まないため、 clock buffersに依存しません。 アイデアとしては、 asynchronous reset signal を直接アクティブ化できる flip-flops をいくつか追加し、これらの flip-flops が reset を同期的に非アクティブ化することです。つまり、 synchronized asynchronous resetです。

たとえば、元の asynchronous reset が @external_resetnの場合、次のような reset が生成されます。

reg pre_rstn1, pre_rstn2;
   reg resetn;

   always @(posedge clk or negedge external_resetn)
     if (!external_resetn)
       begin
         resetn <= 0;
         pre_rstn2 <= 0;
         pre_rstn1 <= 0;
       end
     else
       begin
         resetn <= pre_rstn2;
         pre_rstn2 <= pre_rstn1;
         pre_rstn1 <= 1;
       end

@clk は、 @resetnによってリセットされる synchronous elements によって使用される clock であることに注意してください。

@external_resetn がアクティブ (つまりロー) の場合、3 つの registers すべてが非同期でアクティブ (つまりゼロ) になります。ただし、 @external_resetn が非アクティブ化されると、次の clock edgeでは @pre_rstn1 のみが非アクティブになり、これは後続の clocks 上の @pre_rstn2 および @resetn に伝播します。

2 つの余分な registers の目的は、 @resetn が安全な方法で非アクティブ化されるように、 metastabilityから保護することです。これは、 @external_resetn が @clkと比較して悪いタイミングで非アクティブになり、 @pre_rstn1 で metastable condition になる可能性がある場合に必要です (このページでは metastabilityについて説明します)。

この synchronizer の利点は、 @external_resetn を asynchronous resetとして使用できることです。 clock がアクティブでなくても動作します。ただし、 synchronous elements は同期して非アクティブになる reset signal を受信するため、 timing が確保されます。

言うまでもなく、各 clock には独自の synchronized asynchronous resetが必要です。

上記のように @resetn を生成するだけでは不十分であることに注意してください。 @clk の timing constraints は、 @resetn から synchronous elementsへの paths に適用する必要があります。一部の FPGA ツールのデフォルトは、 synchronous elementの asynchronous reset inputで終わる paths の timing を無視することです。この目的のために、ツールの設定を変更する必要がある場合があります。

したがって、 @resetn が通常の asynchronous resetとして使用される場合、たとえば

always @(posedge clk or negedge resetn)
    if (!resetn) // Are you sure this path is timed?
      the_register <= 0;
    else
      [ ... ]

上記の synchronizer では、 resetからの確実な回復を保証するには不十分です。 @resetn で開始し、 flip-flopsの asynchronous reset inputs で終了する paths が実際に時間調整されていることを確認するのは、ユーザーの責任です。

この synchronizer は、 @external_resetn上の glitches に対しては役に立たないことに注意することも重要です。 @external_resetnの active pulse の長さが、 FPGAの flip-flopsの仕様よりも短い場合、何かが起こる可能性があります。したがって、 @external_resetn は、長い pulseを保証する logic または外部電子機器によって生成される必要があります。これが不可能な場合、唯一の解決策は、 @sync_resetnの次の例のように、 reset を完全に同期することです。

reg pre_rstn1, pre_rstn2;
   reg sync_resetn;

   always @(posedge clk)
     if (!external_resetn)
       begin
         sync_resetn <= 0;
         pre_rstn2 <= 0;
         pre_rstn1 <= 0;
       end
     else
       begin
         sync_resetn <= pre_rstn2;
         pre_rstn2 <= pre_rstn1;
         pre_rstn1 <= 1;
       end

ただし、 @clk が非アクティブな場合、このシンクロナイザーは @external_resetn を無視します。 logic が @external_resetn を asynchronous resetのように扱うことになっている場合、これは問題になります。つまり、 clocks がアクティブでない場合でも動作するはずです。

初代 synchronizerに話を戻しましょう。 @resetn をプレーンな synchronous resetとして使用するのはどうですか?このようなもの:

always @(posedge clk) // @resetn not in sensitivity list!
    if (!resetn)
      the_register <= 0;
    else
      [ ... ]

@resetnの非アクティブ化は timing constraintsによって確実にタイミングがとられるため、これは多かれ少なかれ問題ありません。ただし、 @resetn の非同期アクティベーションは時間指定されていないため、 reset が有効になる直前に、関連する synchronous elements がランダムに動作する可能性があります。この目的には、完全に同期された reset を使用することをお勧めします。たとえば、上記で定義した @sync_resetn です。

このトピックを一般的なコメントで締めくくります。 上記の例では、 active-low resets を使用することを選択しましたが、これは主に、 reset signal が resistorを介して power supply voltage に接続された capacitor で生成された時代から続く伝統によるものです。 capacitor には当初 voltage がなかったため、 reset input は '0'でした。この capacitor はすぐに十分な charge を構築し、その結果、 reset input は '1'に変わりました。この古いタイプの power-up reset が、今日に至るまで多くの resets が active low である理由です。

結論として、 asynchronous reset を確実に使用することは可能ですが、これを達成することは、多くの人が信じているほど単純ではありません。 asynchronous resetの信頼性を確保するための 2 つの方法を紹介しました。 timingの問題を回避するために clocks を一時的にオフにするか、 timingを確保するために synchronizer を使用します。 FPGAsでいつものように、 timing はゲームの名前です。

実世界で asynchronous reset に依存するほとんどの designs では、これらの方法は使用されません。その結果、 FPGA design の信頼性は純粋な運に依存します。

オプション #4: Synchronous reset

synchronous reset は、次のコード パターンで最もよく知られています。

always @(posedge clk)
    if (reset)
      the_register <= 0;
    else
      [ ... ]

後でより良いコード パターンと思われるものを提案しますが、今のところはこれに固執します。とにかく、ここでは active-high reset を選択したことに注意してください。これは、 synchronous resetsでより一般的な選択です。というか、私の感想です。

synchronous reset は、次の点を除いて、ほぼすべての面で asynchronous reset よりも優れています。

FPGAs はアクティブな clock なしで使用されることはめったになく (従来、テストのためにこれを必要とする ASICsとは異なり)、 routing の専用リソースに関する問題が存在するかどうかも明らかではないため、主なトピックに焦点を当てます。 Fan-out.幸いなことに、これは簡単に解決できます。

上記で提案された synchronizer が使用されている場合、同じ fan-out の問題が asynchronous reset にまったく同じ影響を与えることにも言及する価値があります。したがって、 fan-out が synchronous reset の欠点であると本当に言えるのは、 clocks をオフにして asynchronous reset を使用する人 (つまり、 gated) だけです。

fan-out の問題を解決するために頭に浮かぶ最初のことは、 synthesizer constraint または attribute を使用して fan-outを制限することです。ただし、制限に達したときに synthesizer が flip-flop を複製するだけなので、これはあまり好ましくない方法です。したがって、複製された flip-flops の output がまったく異なる目的で modules に移動することがよくあります。したがって、この output の宛先は FPGA全体に分散する可能性があります。これにより、長い routing と重要な propagation delayが発生します。

シンプルで効率的なソリューションは、 logicの重要な部分ごとにローカル reset を作成することです。何かのようなもの

module medium_sized_module (
  input clk,
  input reset,
  input [15:0] in_data
  output [15:0] out_data
);

  (* dont_touch = "true" *) reg local_reset;
  reg the_register;

  always @(posedge clk)
    local_reset <= reset;

  always (@posedge clk)
    if (local_reset)
      the_register <= 0;
    else
      [ ... ]

@local_reset は @reset のローカル コピーであるという考え方です (1 つの clockによって遅延されます)。この module 以下の @reset の代わりに @local_reset を使用すると、 fan-out を妥当なレベルに保つことができます。このローカル reset のコンシューマはいずれにせよ緊密に相互接続されることが予想されるため、 FPGAの特定の領域に配置される可能性があります。したがって、ローカルの reset は logic fabricを経由して長距離を移動する必要はありません。

logicを最適化するために、 synthesizer がローカル reset registers を削除しないようにすることが重要です。 synthesizer は通常、異なる modulesに属していても、同一の動作をする registers がある場合にこれを実行します。上記の例では、 Vivadoの synthesis attribute 、つまり「dont_touch」が表示されています。各 synthesizer には、これを行う独自の方法があります ( Quartusの場合、 synthesis attributeと同じことが「dont_merge」で実現されます)。

synthesizer が実際にすべての registersを保持していることを確認するには、これらすべての registers に同じ名前 (たとえば、上記の local_reset ) を付けてから、 implemented designでこの名前で registers を検索すると便利です。

もちろん、 moduleごとにローカル reset を作成する必要はありません。おおよその数値として、50 ~ 100 の fan-out は、特に FPGAの小さな物理領域内で logic elements に到達する場合に、ローカル resetに妥当です。

fan-out を削減するトピックは、 timing closureのコンテキストでも説明されています。

synchronous resetsの詳細

synchronous resets に関するよくある誤解は、 synthesizer が synchronous resetのコード パターンと一致する Verilog コード パターンに遭遇すると、 reset signal が flip-flopの synchronous reset inputに接続されるというものです。これは事実である場合もありますが、多くの場合はそうではありません。

これは、 flip-flopの asynchronous reset inputに接続する必要がある asynchronous resetとは異なります。そうしないと、 reset は clockなしでは機能しません。

Synthesizers は、コーディング パターンに特別な意味を与えませんが、 Verilog コードから派生した logic equation を計算する傾向があります。次の例を検討してください。

always @(posedge clk)
    if (reset)
      the_register <= 0;
    else if (some_condition)
      the_register <= !the_register;
    else if (some_other_condition)
      the_register <= 0;

このコードを読み取る 1 つの方法は、 always statement が synchronous resetを要求する標準コード パターンで始まり、 registerの動作の特定の定義が続くというものです。したがって、 @reset が関連する flip-flopの synchronous reset input に接続され、 logic function ( LUTとして実装されている) の output が flip-flopの data inputに接続されることも予想できます。

実際には、 synthesizers は通常、次の clock で @the_register の値をできるだけ簡潔に実装します。たとえば、 flip-flopの reset input は、式 (reset || (some_other_condition && !some_condition) )を実装する logic function (つまり LUT) に接続できます。

しかし、さらに興味深い可能性があります。 flip-flopの reset input はまったく使用されない可能性があります。代わりに、 data input のみが使用され、 logic function は @reset signal を inputsの 1 つとして使用します。したがって、 @reset がハイの場合、 logic functionの output はゼロになります。このように、 @reset は確かに @the_register をゼロにしますが、他の信号とは異なる扱いはされません。

繰り返しになりますが、 flip-flop には synchronous reset inputがありますが、 synthesizers は synchronous resetのコード パターンを特別に扱う傾向はなく、 reset 信号も他の信号と区別して扱うことはありません。 flip-flopの reset input は、 Verilog コードで必要な動作を実装するために最適な方法で使用されます。これは、 reset input を reset signalに直接接続することもあれば、 reset signalを含む可能性のある logic function に接続することもあり、 reset input をまったく使用しないこともあります。 synthesizer は、パフォーマンス目標をよりよく満たすのに役立つことだけを行います。

Xilinxの Vivado のユーザーは、 DIRECT_RESET と EXTRACT_RESETの 2 つの synthesis attributesを使用して、この問題をより適切に制御できます。

asynchronous resetsのもう 1 つの欠点でこれを締めくくります。 ほとんどの FPGAsでは、 flip-flop には reset/set inputが 1 つしかありません。この input は、同期または非同期で動作できます。 reset が同期の場合、 synthesizer は、要求された動作を満たすために LUTs の使用を減らすために、この input を使用するトリックを見つける可能性があります。 asynchronous resetがあるとこういう近道はありえない。したがって、 asynchronous reset は synthesizerの手を縛り、より多くの logic resourcesを無駄にすることを余儀なくされます。

registersの偶発的なフリーズを回避する

次のコード例に示すように、 resetsを実装するために一般的に使用されるコード パターンには落とし穴があります。

always @(posedge clk or negedge resetn)
     if (!resetn)
       begin
         reg1 <= 0;
         reg2 <= 0;
         // Ayeee! Forgot to reset reg3 !
       end
     else
       begin
         reg1 <= [ ... ];
         reg2 <= [ ... ];
         reg3 <= [ ... ];
       end

コメントで暗示されているように、 @reg3 は、アクティブな @resetnの begin-end 句には表示されません。その結果、上記の Verilog コードでは、 @resetn がアクティブである限り、 @reg3 が値を変更しないことが必要です。 @reg3 が次のように定義されている場合と同じです。

always @(posedge clk)
     if (resetn)
       reg3 <= [ ... ];

つまり、 @resetn は @reg3の clock enable として機能します。 clock は、 @resetn が高い場合にのみ有効です。

synchronous resetsでもまったく同じことが起こります。

always @(posedge clk)
     if (reset)
       begin
         reg1 <= 0;
         reg2 <= 0;
         // Ayeee! Forgot to reset reg3 !
       end
     else
       begin
         reg1 <= [ ... ];
         reg2 <= [ ... ];
         reg3 <= [ ... ];
       end

これは begin-end 句のペアにすぎず、2 番目の句は @reset が非アクティブな場合にのみ有効になるため、実際には理解しやすいです。したがって、この例の @reg3 の定義は明らかに

always @(posedge clk)
     if (!reset)
       reg3 <= [ ... ];

したがって、明白な (そして必ずしも賢明ではない) 結論は、 resetの begin-end 句で register を忘れないことです。実際、多くの FPGA designers は、必要かどうかにかかわらず、すべての registersをリセットしています。これが唯一の方法だと信じているためです。または、各 register に独自の "always" ステートメントがあるコーディング スタイルを採用しています。

しかし、意図的に一部の registers をリセットし、他の registers をリセットしたくない場合はどうすればよいでしょうか?

asynchronous resetでは、これらの registers を別の "always" ステートメントに入れるしかありません。しかし、 synchronous resetでは、これを解決する簡単な方法があります。

always @(posedge clk)
     begin
       reg1 <= [ ... ];
       reg2 <= [ ... ];
       reg3 <= [ ... ];

       if (reset)
         begin
           reg1 <= 0;
           reg2 <= 0;
           // I don't want to reset reg3, and that's fine!
         end
     end

先頭に "if (reset)" という文があり、"else" ステートメントの下に興味深い部分がある代わりに、"if (reset)" が最後に置かれるため、 reset の割り当てはそれらの前にあるすべてのものを上書きします。

これは上記の例のいずれとも同等ではないことに注意してください。 @reset がアクティブな場合、 @reg1 と @reg2 はリセットされますが、 @reg3 は reset の影響をまったく受けません。

synchronous resetを適用するこの代替方法に不快感を覚える場合は、それを理解できます。それにはいくつかの理由があります。 まず、 FPGA designで一般的に使用されるコーディング パターンに固執することは、一般的に良い方法です。そうしないと、 synthesizer が風変わりなバグを露呈する可能性があります。これは、確立されたコード パターンでは発生する可能性がはるかに低いものです (ゴールデン ルール #4を参照)。したがって、 Verilog 標準ではこの方法が機能することが明示的に要求されていますが、この機能に依存することは必ずしも良い考えではないと主張する人もいるでしょう。

これは強力な議論ですが、価値のあることとして、 synthesizersの広い範囲で、このようなコード パターンを 10 年以上頻繁に使用してきたことをお伝えします。特に、これは私自身のコードで synchronous resets を定義する方法です。私はこれで単一の問題を抱えたことはありません。

この方法を好まないもう 1 つの理由は、 synthesizer が synchronous reset が必要であるというヒントを見逃す可能性があると考えることです。これは、通常のコード パターンではないためです。ただし、すでに上で述べたように、ほとんどの synthesizers はとにかくヒントを取り入れず、 synchronous reset を logicの必要な動作の単なる別の定義と見なします。したがって、この理由には根拠がありません。

ですから、安全だという私の言葉を信じたいのであれば、頭を悩ませることはありません。

asynchronous resetでも同じことができますか?たとえば、これは何をしますか?

always @(posedge clk or negedge resetn)
      begin
        reg1 <= [ ... ];
        reg2 <= [ ... ];

        if (!resetn)
          reg1 <= 0;
      end

これはもちろん、 asynchronous resetの一般的なコーディング パターンからの逸脱です。 Vivadoの synthesizer に関する逸話的なテストで、ヒントが理解され、 @reg1に asynchronous reset が割り当てられていることが明らかになりました。

ただし、 @reg2 に必要な動作は、 FPGAでは実現できません。 上記のように、 @clk と @resetn の両方が clocksであり、 @reg2 がそれぞれ rising edges と falling edgesで新しい値をサンプリングすることを意味します。 2 つの clock inputs を備えた flip-flops は、私が知っているどの FPGA でも利用できないため、 @reg2の定義の synthesis の可能性はありません。

Vivadoの synthesizer はこれに反応して「negedge resetn」の部分を無視し、 @clk のみを clockとして使用する flip-flop を作成しました。 synthesesの結果には、この奇妙な点の痕跡はなく、 synthesizer も warning を発行したり、他の方法で文句を言ったりしませんでした。それは、 synthesizer が Verilog コードで定義されているように動作しない logic を作成したという事実にもかかわらずです。

したがって、特に Vivadoの synthesizerでは、同じコード パターンが実際には asynchronous resetでも機能しますが、これに依存するべきではありません。 Verilog コードは、 logic に実行させたいことを示す必要があります。さもなければ、 synthesizer はそれを自分が好むように誤解する権利があります。

これで、 resetsに関するこのシリーズの 2 ページ目は終了です。次のページでは、 powerup および外部 resetの後に FPGA を起動するさまざまな側面について説明します。

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