このページは、 FPGAsにおける resetsに関する 3 つのシリーズの最初のページです。非常に多くの人が asynchronous resets を実際には期待どおりに動作しないことを知らずに使用しているため、この最初のページでは、トピック全体が思ったほど簡単ではない理由を説明します。
reset は本当に動作しますか?
state machineをリセットするための次の例を検討してください。これは間違っています。
always @(posedge clk or negedge resetn)
if (!resetn)
state <= ST_START;
else
case (state)
ST_START:
begin
state <= ST_NEXT;
[ ... do some stuff maybe? ... ]
end
ST_NEXT:
begin
[ ... do something ... ]
end
endcase
では、 reset の何が問題なのか、疑問に思われるかもしれません。これはまさに教科書に載っている通りです! @state を実装した register を初期状態にする active low asynchronous reset。何が問題になる可能性がありますか?
議論のために、 @resetn signal 自体は問題ないと仮定しましょう。つまり、 pushbutton などに直接接続されていないということです。おそらく、 reset signal を生成するように設計された chip が使用されたか、 reset が FPGAによって内部的に生成されたのでしょう。いずれにせよ、 reset が安定してアクティブになり、十分な時間アクティブのままになり、その後非アクティブになると仮定します。それでもまだ間違っています。
そして、私が間違っていると言うとき、明確な理由もなく FPGA がときどきおかしな動作をするような間違いを意味します。
だから問題は何ですか?まあ、 reset は十分に長くアクティブなので、 @state を確実に初期状態に戻します。しかし、非アクティブになった場合 (つまり、上記の例で '1' に戻った場合) はどうなるでしょうか?その時、関連する flip-flops が @clkの rising edges に応答し始めるはずです。
ただし、 flip-flop が reset signalから回復し、 rising clock edgesで data input のサンプリングを開始するには少し時間がかかります。また、 reset は定義上非同期であるため、 @clkに対していつでも非アクティブ化される可能性があります。
@clk の最初の rising edge が resetの非アクティブ化後すぐに到着した場合、 flip-flop はこの rising edgeを無視します。 @clk に接続されているすべての flip-flops がそれを行う場合、それはまったく問題ありません。しかし、すべての flip-flops がまったく同じというわけではなく、 clock edge を他のものより少し早く取得するものもあれば、 reset の非アクティブ化を他のものより遅く取得するものもあります。
正直なところ、この説明は少し単純化されています。問題のより正確なビューについては、 timingの基本を説明するページの Recovery および Removal に関する部分を参照してください。
したがって、これは次のようになります。 運が悪いと、 reset が clockの rising edgeの近くで非アクティブになり、一部の flip-flops が最初の rising edgeに応答し、他の flip-flops がそれを無視する場合があります。実際、一部の flip-flops では、何をすべきかを決定するのに余分な時間がかかる場合があります。 flip-flops の違いを同じ chip のせいにするか、 clock skew と reset skewのせいにする: 肝心なのは、一部の flip-flops は他の clock cycle よりも 1 つ進んでいるということです。
これがいかに悪いことかを理解するために、上記の例を考えてみてください。 synthesizer がこれが state machineであると認識した場合、 one-hot encodingで state variable を実装する可能性が高くなります。つまり、 stateごとに single-bit register を割り当てます。これらの registers のそれぞれは、 state machine が関連する stateにある場合にアクティブになります。 synthesizer が、 ST_STARTという名前の state には hot_state_0 という名前の register を割り当て、 ST_NEXTには hot_state_1 を割り当てたとしましょう。明らかに、 reset は hot_state_0 をアクティブにし、 hot_state_1を非アクティブにします。
ここで、 state machine が無条件に ST_START から ST_NEXTに移動することに注意してください。したがって、 reset が解放された後、最初の clock で hot_state_0 が非アクティブになり、 hot_state_1 がアクティブになります。
しかし、 reset が不運なタイミングで非アクティブになり、一部の flip-flops が最初の clock edge を逃し、他の flip-flops が逃さない場合はどうなるでしょうか? 1 つの可能性は、 hot_state_0 が最初の clockを見逃しているが、 hot_state_1 がそれに応答することです。その結果、両方がアクティブになります。これは、 one-hot encodingでは不正な状態です。逆の場合、両方の registers が非アクティブになるため、実際には state machine のすべての one-hot registers が非アクティブになります。いずれにせよ、 state machine は合法的な状態に回復できない可能性があります。
これは実際にはどのように見えるでしょうか? もちろん、アプリケーションによって異なりますが、 FPGA に reset を再度適用するまで、何かが正しく動作しない可能性があります。この問題はランダムに発生し、必ずしも頻繁に発生するとは限らないため、この動作の原因を見つけるのは非常に難しい場合があります。また、 compilation と FPGA design の間で動作が異なり、 board と board の間でも動作が異なる可能性があります。つまり、これはあなたを悩ませる可能性のある種類のバグです。問題の原因がこの議論のトピックであるため、この議論ではそれほど悪く聞こえないかもしれません。しかし、このような不安定性が実際に発生すると、何でもあり得、 FPGA に取り憑かれているように感じることがよくあります。
しかし、私はこれを常に行っており、うまくいきます!
それはそう。ほとんどの場合、一部の flip-flops が resetの後の最初の clock を見逃しても、それほど問題にはなりません。
上記の state machine の例が失敗する主な理由は、最初の clock cycleで初期状態のままになるためです。実際の designs のほとんどの state machines には、初期状態から離れる規則があるため、最初の数回の clock cyclesでは常にこの状態のままです。したがって、この間違いを回避できます。
しかし、ここに別の例があります。シンプルな counter:
reg [15:0] counter;
always @(posedge clk or negedge resetn)
if (!resetn)
counter <= 0;
else
counter <= counter + 1;
この場合、 @counter は 16 個の flip-flopsで構成されます。これらの各 flip-flops は、その data inputで次の clock cycle の counter の値を受け取り、その asynchronous reset inputで @resetn の値を受け取ります。
@resetn がアクティブな場合、 @counter の値は 0 になり、次の clock cycle の counter の値は 1 になります。したがって、 counter[0] を除くすべての flip-flops は、最初の clock edge を逃したかどうかに関係なく、ゼロのままになります。したがって、どちらの方法でも正しくカウントが開始されます。このようなコードが記述されるほとんどの場合、 counter が最初の clock cycle を逃したかどうかは問題ではありません。
ただし、これは別の話です。
reg [15:0] counter;
always @(posedge clk or negedge resetn)
if (!resetn)
counter <= 0;
else
counter <= counter - 1;
小さな違いですが、大きな違いがあります。 counter がゼロから始まり、カウントダウンする場合、次の clock cycle での counter の値は 0xffffです。言い換えると、すべての flip-flops は、 reset後の最初の clock で値を変更する必要があります。したがって、 reset後の最初の clock edge に一部が応答し、その他は応答しない場合、 counter は事実上任意のランダムな値から開始できます。
しかし、 counter を値ゼロにリセットしてカウントダウンするのは誰でしょうか?
したがって、より現実的な例を次に示します。 clock enable signal は、 clockの周波数が半分に低下したかのように logic を動作させます (したがって、必要に応じて multi-cycle path を使用できます)。
reg en;
always @(posedge clk or negedge resetn)
if (!resetn)
en <= 0;
else
en <= !en;
always @(posedge clk)
if (en)
[ ... do something ... ]
ここでは何も問題がないと考えることができます。 clock enable、 @enは単一の registerなので、いつトグルが開始されるかは実際には問題ではありません。それとも、問題なのでしょうか? 問題は、 clock enable signal は fan-outが高くなる傾向があるため、 synthesizer は fan-outの制限を超えないようにそれを複製する可能性があることです。
Vivado synthesizer を使用した私の逸話的な実験では、 @en を実装した複製された registers のそれぞれが、独自の output signalに依存していることが示されました。言い換えれば、すべての flip-flops が次の output を決定するために使用する信号は 1 つもありませんでした。むしろ、 clockの rising edge で常に値を変更する多くの独立した flip-flops がありました。したがって、これらの flip-flops が同じ clock cycleでトグルを開始しない場合、それらの outputs は無期限に異なるままになります。
このような事故が発生した場合、 logic は完全に故障する可能性があります。したがって、 asynchronous resetに固執する場合は、少なくともすべての clock enables が単一のソースに依存していることを確認してください。
reg pre_en; // Apply some don't-touch synthesis directive on this
reg en;
always @(posedge clk or negedge resetn)
if (!resetn)
pre_en <= 0;
else
pre_en <= !pre_en;
always @(posedge clk or negedge resetn)
if (!resetn)
en <= 0;
else
en <= pre_en;
always @(posedge clk)
if (en)
[ ... do something ... ]
問題は、 @pre_en が次の値を決定する register であることです。 flip-flop には低い fan-out があり、おそらく synthesizer に改ざんしないように指示する attribute もあります。このようにすると、 registerは確実に 1 つになります。 @en を実装するすべての flip-flops は @pre_enに依存しているため、次の clockの @en の値はすべて一致します。 resetの後の最初の clock については、 flip-flopsの一部が見逃しても問題ありません。最初の clock cycle の @en の値はいずれにしてもゼロだからです。
要するに、 asynchronous reset を誤って使用しても通常は問題ないということです。これは主に、 logic が clockと比較して reset が非アクティブになるタイミングの不確実性に対して通常は寛容であるためです。それにもかかわらず、不注意に asynchronous resets を適用すると、時折不正な動作が発生する可能性があり、これを解決するのは非常に困難です。
reset と clockの間で timing constraint を使用する
resetの非アクティブ化と clockの rising edge 間の不確実なタイミング関係を回避する明白な方法は、 reset signalで timing constraint を使用することです。ただし、そうすることで、 reset signal は同期化されます。
しかし、 reset signal はどのクロックと同期しているのでしょうか? logic design全体に対して 1 つのグローバル asynchronous reset があると便利な場合がよくあります。この reset は、特定の 1 つの clockと同期している logic で作成されます。この reset が、別の clockと同期している logic で使用されると、 clock domain crossingが作成されます。これはそれ自体がトピックですが、最も重要な点は、ツールが関連する paths上の timing を無視する可能性があることです。
したがって、 asynchronous resets で timing constraints を使用するための出発点は、 clockごとに個別の asynchronous reset が必要であるということです。または、どうしても related clocksのグループごとに別の asynchronous reset を使用することをお勧めします。そうでなければ、 timing constraintには意味がありません。奇妙に聞こえるかもしれませんが、それは asynchronous reset が非同期ではなくなったためです。
Verilog コードが asynchronous reset のパターンを使用しているという事実は、違いはありません。 flip-flopの asynchronous reset input が使用されているかどうかも、 flip-flop が reset input を非同期と見なすように構成されているかどうかも問題ではありません。 reset が clockと同期しており、 timing constraint が使用されている場合、 reset は実質的に同期しています。この場合、 Verilog パターンを直接使用することを検討してください。
always @(posedge clk)
if (!resetn)
state <= ST_START;
[ ... ]
とはいえ、 Intelの timing closureに関する YouTube 動画では、 clockと同期する asynchronous resets の使用が推奨されており、 timing constraints も使用する必要があります。 asynchronous reset を選択するのは、 FPGAで global routing 専用のリソースを利用するためです。これはかなり奇妙だと思います。 global routing でも、特に大型の FPGAsでは、大型の delayを使用できるからです。しかし、これが理にかなっているシナリオも確かにいくつかあります。
実際には、 asynchronous reset が効果的に同期している場合でも、 asynchronous reset を使用し続ける別の理由があります。これについては、次のページで説明します。 これにより、 reset signal のアクティベーションを非同期的に伝播することができ、 simulations だけでなく ASICsのテストでも役立ちます。
同期された asynchronous reset でこの方法を使用する場合、 flip-flopsの asynchronous reset inputsに接続するすべての signal paths で timing constraint が強制されるようにすることが重要です。 reset が、宛先の flip-flopと同じ clock と同期している flip-flop によって生成されるという事実は、それらの間の path が時間調整されていること自体を保証しません。デフォルトでは、一部の timing ツールは asynchronous inputsで終わる path を無視するため、この強制を明示的に有効にする必要がある場合があります。これらの paths が実際に timing constraintsでカバーされていることを timing reports で確認してください。これに落ちるのは簡単です。
これで、 resetsに関するこのシリーズの最初のページを終了します。次のページでは、 resets のさまざまなオプションとFPGA の初期化について説明します。