「時々FPGAが動かない!」という再現性の低い不具合に遭遇したことはありませんでしょうか?原因の1つとして、ステート・マシン(Finite State Machine, FSM)のデッドロック(Deadlock)が考えられます。
今回は、実際にステートマシンがデッドロックする回路を作成し、デッドロックを体験してみたいと思います。
なお、ソースコードはGitHubに公開しています。DIGILENT ARTY S7ボードに実装することで、実際に試すことができます。
目次
図1のように12個のステートを持つステート・マシンを作成しました。
回路のブロック図を図2に示します。
この回路では、ステート・マシンが動作していればリセット解除直後(1クロック後)にLEDが赤になります。リセット解除でデッドロックすれば、赤以外になるはずです。
ONE_YEARステート・マシンのコードは下記のようになります。FSM_RSTは「非同期リセット」と呼ばれるリセット入力です。今回の回路では、非同期リセット入力が本当に非同期になっています。人の指で操作するので、11MHzや153MHzに同期することは不可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /* State machine */ always @ (posedge CLK or posedge FSM_RST) begin if (FSM_RST) begin current_state <= JANUARY; end else begin current_state <= next_state; end end always @ (current_state or j2f_trg or trg_redg) begin case (current_state) JANUARY : begin if (j2f_trg == 1'b1) next_state <= FEBRUARY; /* Transition immediately to check if the FSM works */ else next_state <= current_state; end FEBRUARY : begin if (trg_redg == 1'b1) next_state <= MARCH; else next_state <= current_state; end ... ... default : next_state <= JANUARY; endcase end |
ステート・マシンの非同期リセット入力を、本当に非同期にしてはいけないという内容を下記の記事で解説しました。本来は、ステート・マシンの非同期リセット入力は、ステート・マシンのクロックに同期させ、リカバリー・リムーバルを満足する必要があります。
関連記事
ステート・マシンの非同期リセットのリカバリー・リムーバルが満足していないというバグがある今回の回路は、どのように動作するのでしょうか?
図3, 4にタイミング・レポートを示します。ステート・マシンはワン・ホット方式でエンコードされており、12個のフリップ・フロップになっています。
FPGAの入力ポートRST_BTNからフリップ・フロップのリセット入力までの遅延に注目してください。遅延の値が12個のフリップ・フロップで異なっています。つまり、各フリップ・フロップは同時にリセット解除されないということです。クロックのエッジのタイミングと、リセット解除のタイミングの関係が絶妙だと、イリーガルス・テートになります。イリーガル・ステートになると、デッドロックします。
正常動作
まずは、正常動作時の動きです。
デッドロック
こちらがデッドロック発生時です。153MHzを入力したステート・マシンが、リセット解除の直後にデッドロックしました。
動画の中では、リセット&解除を12回行ったところでデッドロックが発生しました。この不具合は必ず発生するわけではなく、再現性が低いです。このため、解析がかなり大変です。
撮影前に試したときは、50回くらいボタン押しました…
このような不具合を避けるため、ステート・マシンのリセット入力はクロック同期にし、リカバリー・リムーバルを満足させるようにしましょう。