FSMとは
有限状態機械は以下を行うコントローラです:
- 任意の時点で、固定された名前付きstateの1つを保持。
- 入力(場合により現在のstateも)に基づいてstate間を遷移。
- 現在のstate(場合により現在の入力も)に依存する出力を生成。
これは驚くほど多くのデジタル設計をカバーします。交通信号コントローラ、UART送信器、メモリコントローラ、ネットワークプロトコルハンドラ、命令デコーダ、離散的な動作モードを持つあらゆるもの。
FSMがデータパスロジックと違って感じる理由は、値 ではなく state であることです。カウンタは1, 2, 3, 4を刻みます。FSMはIDLE, FETCHING, BUSY, DONEを刻みます。形は同じですが、stateには名前があり、遷移に意味があります。
2プロセスパターン
標準のVerilog FSMは 2つのalwaysブロック を使います:
- クロック同期ブロック がクロックエッジ毎にstate registerを捕捉。非blocking代入を使う。小さく、通常3行。
- 組み合わせブロック が現在のstateに対して
caseを使って次stateと出力を計算。blocking代入を使う。「興味深い」コードはここに住む。
この分離は3つの恩恵を持ちます。stateを明示的に考えるよう強制し、「1つのregister + 1つの組み合わせブロック」にきれいにマップするハードウェアを生成し、これが標準であり、あなたのVerilogを読む人が誰でも即座に認識します。
動作する例:シーケンス検出器
古典的な教育用FSM:シリアル入力でビット列1011を検出します。シーケンスが完了したら1サイクルパルスを出力します。
2ブロック構造が上の箇条書きです。きれいに整える細部に注目する価値があります:
- 組み合わせブロックの先頭でのデフォルト。
next_state = state(今いるところに留まる)とdetected = 1'b0(パルスなし)が「何もしない」代入です。それから各case分岐は異なるものだけを設定します。これでラッチを推論することが不可能になります。 - state名の
localparam。 moduleを読む人は3'd0、3'd1、3'd2、3'd3ではなくS0、S1、S2、S3で考えます。合成ツールが置換を行います。 - クロック同期ブロックからの出力なし。 「このstateで何をするか」のロジックはすべて組み合わせブロックに住みます。クロック同期ブロックは現在のstateを保持すること以外何の責任もありません。
Moore vs Mealy
Moore:出力は現在のstateのみに依存。 Mealy:出力は現在のstate と 現在の入力に依存。
上の例では、detectedはS3分岐内で、inが予期されるシーケンス完了パターンの1つに一致するときだけ セット されます。これはMealy出力で、stateに加えてinに依存します。Moore版では「今検出した」用の別stateがあり、そのstateが現在のときにdetected = 1をセットします。出力は1サイクル遅れますが、inのグリッチには反応しません。
両スタイルとも妥当です。Mooreは、サイクル中に入力が変わってもグリッチしないことが保証されるので、教科書のデフォルトです。Mealyは速く(入力駆動の出力にregisterレイテンシなし)、多くの場合より小さなハードウェアを生成します。実装するプロトコルに基づいて選んでください。
State Encoding:バイナリ、one-hot、グレイ
各stateに割り当てるビットパターンは、面積と速度に影響します:
- バイナリ(
S0 = 3'd0、S1 = 3'd1、...):最小のstate register - 個のstateにビット。デコードロジックが最大。 - One-hot(
S0 = 4'b0001、S1 = 4'b0010、...):N stateにNビット。デコードロジックは自明(各stateは1本のwire)。遷移が速い。FPGAはしばしばこれをデフォルトに。 - グレイコード:連続stateが1ビットだけ異なる。stateビットがクロックドメインを跨ぐときに有用。
ほとんどのモダンな合成ツールはencodingを選んでくれます(Vivado、Quartus、Design Compilerすべてが各々を試して最良を選ぶ自動モードを持ちます)。指定する必要はめったにありません。指定するときは、ほとんどのツールがattributeアノテーションや(* fsm_encoding = "one_hot" *)プラグマを受け入れます。
3ブロックのバリアント
時折、FSMが 3 つに分割されているのを見ます。stateのクロック同期ブロック、次stateの組み合わせブロック、出力の組み合わせブロック。これは出力計算を独自のブロックに引き出した2ブロックパターンです:
// State register
always @(posedge clk) ...
// 次stateロジック
always @(*) ...
// 出力ロジック
always @(*) begin
case (state)
...
endcase
end
出力分離スタイルは、出力が大きく、次stateロジックがそれらと同じブロックにあると散らかるときに有用です。小さなFSMにはやり過ぎです。
FSMでdefaultが行うこと
すべてのFSMのcase文はdefault分岐を持つべきです。2つの理由:
- 安全性:state registerがどうにか無効値(破損、バグ、部分reset)を取った場合、
defaultは既知のstateに戻します。 - 合成ヒント:明示caseが網羅的なとき(例えば、2ビットstateで4値すべて処理)、
default: next_state = 'x;は合成ツールに「defaultは到達不能と約束する、自由に最適化して」と伝えます。到達不能パスがシミュレーションで踏まれた場合、結果のxが伝播してバグが即座に表面化します。
default: begin
next_state = S0; // 安全な復旧
// または
next_state = 'x; // 到達不能、自由に最適化
end
defaultが本当に到達不能だと証明したかに基づいて選んでください。
注意すべきこと
組み合わせブロックの先頭でデフォルトを忘れる。 next_state = stateと出力のデフォルトなしでは、すべてを代入しない分岐がラッチを漏らします。
クロック同期ブロックに出力を入れる。 detected <= 1がalways @(posedge clk)ブロックに住むと、出力がregister化されます。1サイクル遅れて現れます。これは意図的なこともあります(「register化されたMealy」出力)が、仕様が即時パルスを要求するときは一般的な偶発設計ミスです。
blockingと非blockingを混在させる。 クロック同期ブロック:<=。組み合わせブロック:=。1つのブロック内で2つを混在させるのは競合状態です。
next_stateを参照してstateを代入する組み合わせalwaysブロック。 シミュレータが解決できないフィードバックループを構築します。クロック同期ブロックがstateを所有し、組み合わせブロックがnext_stateを所有します。どちらも他方の変数に触れないでください。
次に読むもの
これで記述できる任意のコントローラを構築できます。次の章は合成可能設計から一歩後退して、それを動かすtestbenchを扱います。刺激を駆動し、出力を観察し、波形をダンプし、moduleが本当に思った通りに動くか検証する方法。
よくある質問
Verilogの有限状態機械とは?
FSMは、いくつかの名前付きstateの1つを保持し、入力に基づいてstate間を遷移するコントローラです。Verilogの標準実装は2つのブロックを持ちます:クロックエッジごとにstate registerを更新するクロック同期alwaysと、現在のstateと入力に基づいて次stateと出力を計算する組み合わせalways。
Verilogの標準FSMパターンとは?
2プロセスFSM:1つのクロック同期always @(posedge clk)ブロックがstate registerを保持し非blocking代入を使い、1つの組み合わせalways @(*)ブロックが現在のstateに対してcaseを使って次stateと出力を計算します。この分離はコードを読みやすく、lintしやすく、きれいに合成可能にします。
MealyとMoore FSMの違いは?
Moore FSMでは、出力は現在のstateのみに依存します。Mealy FSMでは、出力は現在のstateと現在の入力の両方に依存します。Mealyマシンは1サイクル速く反応します(入力依存の出力にregisterレイテンシなし)が、サイクル中に入力が変わるとグリッチを生成することがあります。Mooreマシンは1サイクル遅いですが、より予測可能で、速度が必要でない限りデフォルトの選択です。
Verilogでstateをencodeするには?
module内でlocalparam定数を使います:localparam IDLE = 3'd0;など。3つの一般的なencoding:バイナリ(state 0, 1, 2, ... - 最小のstate register)、one-hot(state毎に1ビット、遷移ごとのロジック層が少ない)、グレイコード(連続するstateが1ビットだけ異なる - グリッチを最小化)。合成ツールが通常選んでくれます。明示的にする必要はめったにありません。