01signal.com

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

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

何よりもまず: resetとは?

reset が何であるかは誰もが知っています。 PCのボタンを押すだけで、すべてがクリーンから始まるのと同じです。これは、 chip に送られる信号であり、これまでに何が起こったとしても、これからはすべてが正常であることを保証します。 reset を、システムを既知の状態にする信号と呼ぶ人もいます。

FPGA designersにとって、 reset は多くの場合、すべての module に追加され、繰り返されるコード パターンで使用される余分な input です。この reset 信号が実際に保証するものに注意を払う必要はありません。

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

このトピックを特に難しくしているのは、運が関係していることです。 一般的に言えば、 reset がアクティブ化されたときのシステムの状態は不明でランダムであり、 resetの非アクティブ化のタイミングも同様です。したがって、 reset 信号の不適切な処理は、まったく別の問題のように思われるまれな誤動作をランダムに発生させる可能性があります。同様に、通常魔法で処理される時折の問題を除いて、目に見える結果なしにこの問題を無視することは可能です.

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

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

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

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

この問題に対する一般的な間違った解決策は、すべての X'sを取り除くために、 design内のすべての registers に asynchronous reset を配置することです。これは通常、シミュレーションの開始時に reset を短時間有効にすることによって行われます。その結果、すべての registers が既知の値を取得し、すべてが完璧に見えます。残念ながら、この誤った解決策は、前のページで説明した問題を隠してしまい、クリーンなスタートを切ったかのような錯覚を与えることがよくあります。

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

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

結論として、 resets はハードウェアを念頭に置いて使用する必要があり、シミュレーション中に煩わしい X's を取り除くためではありません。また、ハードウェアに焦点を当てる必要があるため、シミュレーションについて私が言わなければならないのはこれらの言葉だけです。

リセットの戦略

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

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

私の意見

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

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

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

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

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

一部の registers には、 reset も初期値も必要ありません。これは、特に shift registers およびその他の delay 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 をリセットまたは初期化しない機会は、その有効性を示す付随するフラグまたは状態がある場合に簡単に認識されます。または、最初に 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 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 に応答し、他の同期要素が timingの違反によりこの clock edge を見逃す可能性があります。このシリーズの最初のページで説明したように、これは厄介なバグを引き起こす可能性があります。

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

初期値の設定には制限がありますが、多くのシナリオでは十分であり、明示的な reset は必要ありません。また、場合によっては、 reset 信号が利用できないため、選択の余地がありません。たとえば、 FPGA がウェイクアップした直後に FPGAの reset 信号を作成する 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 の間のタイミングに関する要件があることです。このタイミング要件に違反すると、 clock buffer は glitch ( clock上の FPGAの要件に違反する短い pulse ) を出力する可能性があります。これにより、この clockに依存するすべての synchronous elements の予期しない動作が発生する可能性があります。

残念ながら、 FPGA ベンダーが提供するドキュメントには、このタイミング要件を保証する方法が常に説明されているわけではありません。その結果、 reset の後の最初の clock edge が正しく動作することを保証できない場合があります。そして、最初の clock edgeの保証がなければ、 reset は無意味です。

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

asynchronous reset を確実に適用するための代替方法があり、一般的に提案されています。この方法は clock gatingを含まないため、 clock buffersに依存しません。 アイデアは、 asynchronous reset 信号のアクティベーションを直接通過させるいくつかの 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 信号を受信するため、 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 を使用することを選択したのは、主に resistorを介して power supply voltage に接続された capacitor で reset 信号が生成された時代に由来する伝統のためです。 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 optimizationのために、 synthesizer がローカルの reset registers を削除しないようにすることが重要です。これは、異なる modulesに属していても、同じ動作をする registers がある場合に synthesizer が通常行うことです。上記の例では、 Vivadoの synthesis attribute が表示されています。つまり、「dont_touch」です。各 synthesizer には、これを行うための独自の方法があります ( Quartusの場合、「dont_merge」で synthesis attributeと同じことが達成されます)。

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 信号を 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 信号を 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 信号に直接接続することを意味する場合もあれば、 reset 信号を含む 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に非同期 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. (c3c02857)