今回はステート・マシン(Finite State Machine, FSM)のステートがどのようにエンコードされるかについて解説します。注意しないと、ユーザーの想定とは異なる回路になってしまいますので、ぜひご覧ください。
各エンコード方式の違いと、XilinxのVivado、Intel (Altera)のQuartusにおけるエンコード方式の設定についても解説します。
目次
まずは、ステート・マシンの書き方の一例を示します。
(1) まずは、ステートを定義します
parameter JANUARY = 4'd0; parameter FEBRUARY = 4'd1; parameter MARCH = 4'd2; parameter APRIL = 4'd3; parameter MAY = 4'd4; parameter JUNE = 4'd5; parameter JULY = 4'd6; ... |
(2) ステートを保持するレジスタを定義します
reg [3:0] current_state; reg [3:0] next_state; |
(3) ステートが遷移する条件を記述します
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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; else next_state <= current_state; end FEBRUARY : begin if (trg_redg == 1'b1) next_state <= MARCH; else next_state <= current_state; end ... endcase end |
9~15行目を見ると、現在のステート(current_state)がJANUARYのとき、
となります。このようにして、ステートが遷移するロジックができます。
上記の記述では、図1のように4 bitのフリップ・フロップを使った回路が出来上がるように思われます。
しかし、実際はRTLで書いたような回路にはなりません。
図2にXilinxのVivadoでの合成結果を示します。
FSM_onehot_current_state_reg[*]というフリップ・フロップ12 bit分でステートを保持しています。つまり、表1のようになっています。
ステート | RTL記述 | 合成結果 |
JANUARY | 4’b0000 | 12’b0000_0000_0001 |
FEBRUARY | 4’b0001 | 12’b0000_0000_0010 |
MARCH | 4’b0010 | 12’b0000_0000_0100 |
APRIL | 4’b0011 | 12’b0000_0000_1000 |
このように、論理合成ツールが「あ、これはステート・マシンだね!」と思うと(ステート・マシンを推定すると)勝手にエンコード方式を変えてしまいます。
今回の合成結果のエンコード方式はワン・ホット(One-hot)といいます。
“勝手に”と書きましたが、ユーザーが設定可能です。記事の最後に設定方法を記載します。
代表的なステート・マシンのエンコード方式は下記になります。
各エンコード方式は表2のようになります。
No. | Sequential | Gray | Johnson | One-hot |
0 | 000 | 000 | 0000 | 00000001 |
1 | 001 | 001 | 0001 | 00000010 |
2 | 010 | 011 | 0011 | 00000100 |
3 | 011 | 010 | 0111 | 00001000 |
4 | 100 | 110 | 1111 | 00010000 |
5 | 101 | 111 | 1110 | 00100000 |
6 | 110 | 101 | 1100 | 01000000 |
7 | 111 | 100 | 1000 | 10000000 |
各エンコード方式の特徴は下記のようになります。
ステートを保持するためのフリップ・フロップ数が最小になります。一方、組合せ回路が大きくなりやすいため、組合せ回路の遅延が大きく、動作速度が遅くなる傾向があります。ステートの遷移の際、複数のビットがトグルするため、消費電力が大きくなります。
連続したステートの遷移で変化するビットが1bitだけなので、消費電力が小さいです。図3のような、分岐の無いステート・マシンに適しています。また、ステート遷移のときのグリッチを防ぐことができます。
グリッチとは… 例えば、シーケンシャルのNo.3→No.4の遷移では、011→100で3bitが変化します。このとき、遷移ロジックの出力(フリップ・フロップの入力)が、
011 → (一瞬) 111 → 100
となることがあります。この111がグリッチです。グレイとジョンソンでは、連続したステート遷移で変化するビットは1bitなので、グリッチの発生を防ぐことができます。
ステート遷移で変化するビットが必ず2bitなので、比較的消費電力が小さいです。また、組合せ回路が小さくなるため、動作速度は最速のエンコード方式です。一方、ステートを保持するフリップ・フロップの数が多くなるため、多くのフリップ・フロップを消費します。一般的に、FPGAはフリップ・フロップが多めなので、ワン・ホット方式はFPGAに適していると言えます。ただし、デッドロックする場合があるため注意が必要です。
Tools→Settingsで、Synthesisタブ内の-fsm_extranctionでエンコード方式を選択します。デフォルトはautoです。
autoにすると、Vivadoが最適なエンコード方式を選択してくれます。Xilinxのマニュアルには、32bitまではワン・ホットになるような記載があるので、多くのデザインではワン・ホットになると考えられます。
Assignments→Settingsで、Compiler Settings内のAdvanced Settings (Synthesis)…をクリック。これで開いた設定画面のState Machine Processingでエンコード方式を選択します。デフォルトはAutoです。
Autoでは、FPGAの場合はワン・ホットになります。
上記のように、Vivado、Quartusともに、エンコード方式を設定しない場合のデフォルトはAutoです。この時、多くデザインではワン・ホット方式でエンコードされます。
ワン・ホット方式はデッドロックする場合があるため、リセット系統をちゃんと設計する必要があります。
つまり、全くの無意識でステート・マシンを組んでしまうと、デッドロックして一切動かなくなってしまう可能性があります。