ステートマシンを回路で書く
(1)ここで学ぶ内容
- CDトレイの制御回路を例に、記述の詳細やステートレジスタの構成方法について学ぶ
目標
以下の項目を理解し説明できる
- ステートマシンの回路構造を理解して説明できる
- ステートマシンの回路を記述できる
- ステートマシンを用いて制御回路を設計できる
- ステートレジスタの構成方法を理解し説明できる
修了判定1
2種類のステートマシン記述をシミュレーションして、動作を確認する
修了判定2
2種類のステートマシン記述を論理合成して、回路規模を比較する
ステートマシンをHDLで記述すると簡単にかけます。HDLは便利だと感じると思います。もし、ステートマシンの基本を覚えていない場合はL4ユニットをみてください。
(2)ステートマシンの例
[ ■ ] [ ▶ ] [ ⏸ ] [ ⏮ ] [ ⏭ ] [ ⏏ ]
stateDiagram-v2
closed : 閉じた状態
opening : open中
opened : トレイが出た状態
closing : close中
closed --> closed
closed --> opening : ボタンが押された
opening --> opening
opening --> opened : 完全に開いた
opened --> opened
opened --> closing : ボタンが押された
closing --> closing
closing --> closed : 完全に閉じた
このユニットではL4ユニットで紹介したCDプレイヤーのトレイ制御を例にステートマシンの回路記述を解説します。
動作を簡単に紹介します。
このステートマシンは4つの状態があります。入力はイジェクトボタンで、出力はモータのON、OFFと回転方向です。
閉じた状態でボタンを押されるとモータをONします。完全に開けばトレイが出た状態になり、モータをOFFします。ここでボタンが押されるとモータをONします。
完全に閉じればトレイが閉じた状態になり、モータをOFFします。
次からのページではこのステートマシンをHDLで記述してみます。
(3)トレイ制御回路の入出力とステート名
トレイ制御回路を設計するために、回路仕様を整理してみます。まず入出力信号です。
| 信号名 | 機 能 | |
|---|---|---|
| 入力 | BUTTON | イジェクトボタンの入力 |
| FINOPN | 完全に開いた | |
| FINCLS | 完全に閉じた | |
| 出力 | MOTOR | モータ制御 |
| DIR | 回転方向( 1でトレイが開く、0でトレイが閉じる ) |
入力は3種類あります。これらにより回路の内部状態が変化します。
出力は2種類あります。モータの制御信号と回転方向を示す信号です。
- すべて Highアクティブ( 1で動作 )
- 入力はクロックに同期した 1周期分のパルス
- 上記以外に、 クロック CLK 、 リセット RES がある
次にステートマシンの各状態に対し、ステート名をつけます。
| ステート名 | 状 態 |
|---|---|
| CLOSED | 閉じた状態 |
| OPENING | open中 |
| OPENED | 開いた状態 |
| CLOSING | close中 |
4つの状態に対しこのように名前をつけました。
トレイ制御回路をリセットしたときは、トレイが閉じた状態を初期値とします。
以上の信号名、ステート名をつかってトレイ制御回路を記述します。
(4)ステートマシンの記述
ステートマシンは現在の状態を保持するフリップフロップと、次の状態を決める組み合わせ回路から構成されます。
現在の状態
┌───────────────────────────────┐
│ │
│ ┌────────────┐ │ リセット
│ │ │ │ │
└─>│ 組み合わせ │ 次の状態 V V 出力
入力 ──>│ 回路 ├──────────> ┌──────┐ ──>
│ │ │ FF │
└────────────┘ │ │
└─┬────┘
│
クロック
このフリップフロップをステートレジスタと呼びます。さらにこの組合せ回路をステート生成回路と呼ぶこととします。
ステート生成回路は現在の状態と入力から次の状態をつくります。したがってalways文をつかってこのように記述します。@のあとにはステート生成回路の入力信号をすべて記述します。
// ステート生成回路の記述
always @ ( <現在の状態> or <入力> )
begin
<現在の状態> による多方向分岐
<次の状態> への代入
end
// ステートレジスタの記述
always @ ( <クロック> or <リセット> )
begin
if ( <リセット> )
<現在の状態> の初期化
else
<現在の状態> へ <次の状態> を代入
end
always文の本体には現在の状態による多方向分岐を記述し、分岐先で次の状態を決めます。通常多方向分岐にはcase文を使います。
現在の状態を保持するステートレジスタはステート生成回路で作成した次の信号を入力とするフリップフロップです。クロックは常に与えますので、ステートレジスタの出力はフィードバックして入力にもどる構造になっています。
ステートレジスタの記述はこのように記述します。リセット信号で現在の状態を初期化し、リセット信号がなければクロックのエッジで、現在の状態を次の状態に更新します。
(5)トレイ制御回路の記述 1
まず最初にステート生成回路を記述してみます。
現在の状態
2 ┌─────────┐
/ ─┤CURRENT │ 次の状態
│ NEXT ├─ / ─
┌─────┤BUTTON │ 2
入力┼─────┤FINOPN │
└─────┤FINCLS │
└─────────┘
parameter CLOSED=2'b00, OPENING=2'b01, //ステート値
OPENED=2'b10, CLOSING=2'b11, //ステート値
UNKNOWN=2'bxx;
reg [1:0] CURRENT, NEXT; //ステートレジスタの出力と入力
always @( CURRENT or BUTTON or FINOPN or FINCLS )
begin
case ( CURRENT ) //ステートの移動
CLOSED: if ( BUTTON )
NEXT <= OPENING;
else
NEXT <= CLOSED;
OPENING:if ( FINOPN )
NEXT <= OPENED;
else
NEXT <= OPENING;
OPENED: if ( BUTTON )
NEXT <= CLOSING;
else
NEXT <= OPENED;
CLOSING:if ( FINCLS )
NEXT <= CLOSED;
else
NEXT <= CLOSING;
default:NEXT <= UNKNOWN;
endcase
end
入力は現在の状態CURRENTと、これらの3つの信号(BUTTON、FINOPN、FINCLS)です。出力は次の状態NEXTです。記述を示します。ここでは主要な部分だけ示します。
parameter宣言を使って4つの状態に値を割り当てます。0から3までの値を単純に割り当てました。
CURRENTとNEXTを2ビットでレジスタ宣言します。CURRENTはステートレジスタですので、論理合成後はフリップフロップになります。NEXTは組み合わせ回路の出力になります。
ステートの移動はcase文を使って記述します。現在の状態CURRENTの値により、4方向に分岐します。
例えばトレイが閉じた状態のとき、イジェクトボタンが押されていれば次のステートをトレイのオープン中にします。押されていなければ次のステートをトレイの閉じた状態にします。これは現在の状態を保持することになります。
以上と同じようにステートの移動を記述しています。case文で記述しましたので、defaultでは不定値を代入しています。
(6)トレイ制御回路の記述 2
前ページに続いて、ステートレジスタとモータ制御出力を記述します。
┌─────────┐
│RES │
2 / │ │ 2
─── ─┤NEXT │─ / ─┬─
│ CURRENT ├─────┘
│ │ │
──────┤>CLK │ │
└─────────┘ │
│
┌──────────────────┘
│
│ ┌─────────┐
│ │ MOTOR ├─────
└──────┤CURRENT │
│ DIR ├─────
└─────────┘
モータ制御出力
// ステートレジスタ ( CURRENT )
always @( posedge CLK or posedge RES )
begin
if ( RES )
CURRENT <= CLOSED;
else
CURRENT <= NEXT;
end
// モータ制御出力
assign MOTOR = (CURRENT==OPENING) ||
(CURRENT==CLOSING) ;
assign DIR = (CURRENT==OPENING) ;
ステートレジスタはステート生成回路の出力NEXTを入力し、現在の状態CURRENTを出力します。したがって記述はこのようにとてもシンプルです。
リセット信号で出力CURRENTを初期状態にします。リセット信号がなければ出力CURRENTに入力NEXTを代入しています。
トレイ制御回路として最終的に必要なのはモータ制御出力です。この回路の場合、ステートレジスタの出力からこれらの信号をつくりだしています。
モータがONするのはトレイのオープン中とクローズ中です。したがってこの二つのステートでモータ出力が1になるように等号演算を用いて記述しています。
モータの回転方向を示す信号は1がトレイを開く方向、0がトレイを閉じる方向です。したがってオープン中のステートだけ1にしています。
(7)ステートレジスタの構成
ステートレジスタの構成にはいろいろな方法があります。
エンコード/デコード方式
2
┌── / ──────────────────┐
│ │
V ┌────────┐ │
┌───┤ ステート │ │ ┌────────┐
│ │ レジスタ │ 2 │ │ │─> ステート0
入力 │生 │ │D Q ├─/─┤ステート│─> ステート1
───>│成 │ 2 ┌─>│ │ │デコーダ│─> ステート2
│回 ├─ / ─┤ └────────┘ │ │─> ステート3
│路 │ │ └────────┘
└───┘ └─ クロック
- ステート値をエンコードして保持する
- FFは少ないが、ステート数が多いと動作速度が遅くなる
- ステート値の割り当てが動作速度に影響する
ワンホット方式
4 ステートレジスタ
┌── / ───────┬────────────┐
│ │ ┌──────┐ │
V │ │D CK Q├─┼─> ステート0
┌───┐ │ └─┬────┘ │
入力 │生 │ │ ┌─┴────┐ │
───>│成 ├────────┼─>│D CK Q├─┼─> ステート1
│回 │ │ └─┬────┘ │
│路 │ │ ┌─┴────┐ │
└───┘ ├─>│D CK Q├─┼─> ステート2
│ └─┬────┘ │
│ ┌─┴────┐ │
└─>│D CK Q├─┴─> ステート3
└─┬────┘
クロック ────────────────┘
- 1ステートに1つのFFを割り当てる
- FFが多数必要だが、動作速度は速い
- ステートレジスタのうち1つだけが1になる
前ページで紹介した記述例はエンコード/デコード方式です。この方式はステート値をエンコードして保持します。したがって4ステートのトレイ制御回路ならステートレジスタは2ビットで足ります。個々のステートの信号が必要ならステートレジスタの出力をデコードします。
エンコード/デコード方式は使用するフリップフロップは少なくてすみます。しかし、ステートの数が多くなるとステートデコーダなどの遅延が大きくなりので動作速度が遅くなります。また、ステート値の割り当てによって動作速度が変わることもあります。
もう一つの構成がワンホット方式です。これは1つのステートに1つのフリップフロップを割り当てたものです。4ステートなら4つのフリップフロップが必要になります。
ステートの数が増えるとエンコード/デコード方式に比べ、多数のフリップフロップを必要とします。しかしデコード回路などが不要ですので動作速度は速くなります。動作時にはステートレジスタのうち1つだけが1になり、他はすべて0になります。状態の移動により1になるレジスタが移動します。
もふねこ:
エンコード方式とワンホット方式、どっちがいいの?ってよく聞かれるんだけど、「回路を小さくしたいならエンコード」「速度を重視するならワンホット」って覚えておくと便利だよ🐾
最近のFPGAはFF(フリップフロップ)がたくさんあるから、ワンホット方式が好まれることも多いんだよ!
(8)ワンホット方式の記述
トレイ制御回路をワンホット方式で書き換えてみます。
現在の状態
4 ┌─────────┐
/ ─┤CURRENT │ 次の状態
│ NEXT ├─ / ─
┌─────┤BUTTON │ 4
入力┼─────┤FINOPN │
└─────┤FINCLS │
└─────────┘
parameter CLOSED=4'b0001, OPENING=4'b0010,
OPENED=4'b0100, CLOSING=4'b1000,
UNKNOWN=4'bxxxx;
reg [3:0] CURRENT, NEXT; //ステートレジスタの出力と入力
always @( CURRENT or BUTTON or FINOPN or FINCLS )
begin
case ( CURRENT ) //ステートの遷移
CLOSED: if ( BUTTON )
NEXT <= OPENING;
else
NEXT <= CLOSED;
OPENING:if ( FINOPN )
NEXT <= OPENED;
else
NEXT <= OPENING;
OPENED: if ( BUTTON )
NEXT <= CLOSING;
else
NEXT <= OPENED;
CLOSING:if ( FINCLS )
NEXT <= CLOSED;
else
NEXT <= CLOSING;
default:NEXT <= UNKNOWN;
endcase
end
ステートレジスタが2ビットから4ビットに変わりますので、回路全体が影響を受けます。しかし、この記述スタイルでは宣言部分を書き換えるだけです。回路記述の本体には変更するところはありません。
まずCURRENTとNEXTのビット幅を4ビットにします。次にステート値を4ビットの値にします。ステート値は4ビットのうち、1つだけを1にした値にします。ステートの移動は1になっているビットが移動することで実現します。
ステートマシンの本体の記述はエンコード/デコード方式と同じです。これ以外のステートレジスタとモータ制御回路の記述は変わりません。
(9)修了判定 1
- 本文の中で紹介した 2種類のステートマシンをシミュレーションして動作確認する。
- ファイル名は以下の通り。
| 回路記述 | テストベンチ | |
|---|---|---|
| エンコード/デコード方式 | tray_encdec.v |
encdec_test.v |
| ワンホット方式 | tray_onehot.v |
onehot_test.v |
CURRENT
┌───────────────────────┐
│ RES │
│ │ │
V ┌────┐ V ┌────┐ │ ┌────┐
BUTTON ──────>│ステ│ NEXT │ステ│ │ │組み│──> MOTOR
FINOPN ──────>│ート├──────>│ート├──┴───>│合わ│
FINCLS ──────>│生成│ │レジ│ │せ回│──> DIR
│回路│ ┌─>│スタ│ │路 │
└────┘ │ └────┘ └────┘
│
CLK
シミュレーション結果(エンコード/デコード方式)
Compiling source file "tray_encdec.v"
Compiling source file "encdec_test.v"
0 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE=UNKNOWN MOTOR=x DIR=x
1000 RES=1 BUTTON=0 FINOPN=0 FINCLS=0 STATE= CLOSED MOTOR=0 DIR=0
2000 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE= CLOSED MOTOR=0 DIR=0
3000 RES=0 BUTTON=1 FINOPN=0 FINCLS=0 STATE= CLOSED MOTOR=0 DIR=0
3500 RES=0 BUTTON=1 FINOPN=0 FINCLS=0 STATE=OPENING MOTOR=1 DIR=1
4000 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE=OPENING MOTOR=1 DIR=1
8000 RES=0 BUTTON=0 FINOPN=1 FINCLS=0 STATE=OPENING MOTOR=1 DIR=1
8500 RES=0 BUTTON=0 FINOPN=1 FINCLS=0 STATE= OPENED MOTOR=0 DIR=0
9000 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE= OPENED MOTOR=0 DIR=0
13000 RES=0 BUTTON=1 FINOPN=0 FINCLS=0 STATE= OPENED MOTOR=0 DIR=0
13500 RES=0 BUTTON=1 FINOPN=0 FINCLS=0 STATE=CLOSING MOTOR=1 DIR=0
14000 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE=CLOSING MOTOR=1 DIR=0
18000 RES=0 BUTTON=0 FINOPN=0 FINCLS=1 STATE=CLOSING MOTOR=1 DIR=0
18500 RES=0 BUTTON=0 FINOPN=0 FINCLS=1 STATE= CLOSED MOTOR=0 DIR=0
19000 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE= CLOSED MOTOR=0 DIR=0
End of simulation 15/10/30 14:18:09
波形では、CURRENT[1:0] および NEXT[1:0] が 00 → 01 → 10 → 11 → 00 の順に遷移していることが確認できます。
|0 |5000 |10000 |15000 |20000
CLK ─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_
RES ──┐ └──────────────────────────────────────
BUTTON ─────┐ └──────────────┐ └──────────────────────
FINOPN ─────────────────┐ └────────────────────────────────
FINCLS ─────────────────────────────────────────────┐ └────
CURRENT[1:0] xx│ 00 │ 01 │ 10 │ 11 │ 00
NEXT[1:0] xx│ 00 │ 01 │ 10 │ 11 │ 00
MOTOR ─────────────┐ └──────────┐ └──────
DIR ─────────────┐ └───────────────────────────
CLOSED(00) → OPENING(01) → OPENED(10) → CLOSING(11) → CLOSED(00)
シミュレーション結果(ワンホット方式)
Compiling source file "tray_onehot.v"
Compiling source file "onehot_test.v"
0 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE=UNKNOWN MOTOR=x DIR=x
1000 RES=1 BUTTON=0 FINOPN=0 FINCLS=0 STATE= CLOSED MOTOR=0 DIR=0
2000 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE= CLOSED MOTOR=0 DIR=0
3000 RES=0 BUTTON=1 FINOPN=0 FINCLS=0 STATE= CLOSED MOTOR=0 DIR=0
3500 RES=0 BUTTON=1 FINOPN=0 FINCLS=0 STATE=OPENING MOTOR=1 DIR=1
4000 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE=OPENING MOTOR=1 DIR=1
8000 RES=0 BUTTON=0 FINOPN=1 FINCLS=0 STATE=OPENING MOTOR=1 DIR=1
8500 RES=0 BUTTON=0 FINOPN=1 FINCLS=0 STATE= OPENED MOTOR=0 DIR=0
9000 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE= OPENED MOTOR=0 DIR=0
13000 RES=0 BUTTON=1 FINOPN=0 FINCLS=0 STATE= OPENED MOTOR=0 DIR=0
13500 RES=0 BUTTON=1 FINOPN=0 FINCLS=0 STATE=CLOSING MOTOR=1 DIR=0
14000 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE=CLOSING MOTOR=1 DIR=0
18000 RES=0 BUTTON=0 FINOPN=0 FINCLS=1 STATE=CLOSING MOTOR=1 DIR=0
18500 RES=0 BUTTON=0 FINOPN=0 FINCLS=1 STATE= CLOSED MOTOR=0 DIR=0
19000 RES=0 BUTTON=0 FINOPN=0 FINCLS=0 STATE= CLOSED MOTOR=0 DIR=0
End of simulation 15/10/30 14:20:22
波形では、CURRENT[3:0] および NEXT[3:0] が 0001 → 0010 → 0100 → 1000 → 0001 の順に遷移していることが確認できます。
|0 |5000 |10000 |15000 |20000
CLK ─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_┌─┐_
RES ──┐ └──────────────────────────────────────
BUTTON ─────┐ └──────────────┐ └──────────────────────
FINOPN ─────────────────┐ └────────────────────────────────
FINCLS ─────────────────────────────────────────────┐ └────
CURRENT[3:0] xx│ 0001 │ 0010 │ 0100 │ 1000 │ 0001
NEXT[3:0] xx│ 0001 │ 0010 │ 0100 │ 1000 │ 0001
MOTOR ─────────────┐ └──────────┐ └──────
DIR ─────────────┐ └───────────────────────────
CLOSED(0001) → OPENING(0010) → OPENED(0100) → CLOSING(1000) → CLOSED(0001)
(10)修了判定 2
- 本文の中で紹介した2種類のステートマシンを論理合成し、回路規模を比較する。
- 論理合成後、レポートファイルにより回路規模を調べ、以下の空欄に記入する。
エンコード/デコード方式 (TRAY_ENCDEC)
****************************************
レポート : area
回路 : TRAY_ENCDEC
****************************************
ポート数: 7
ネット数: 18
セル数: 11
セル種類: 7
組み合わせ回路: 12
非組み合わせ回路: 18
合計: 30
****************************************
レポート : timing
回路 : TRAY_ENCDEC
****************************************
ライブラリ: hd350s
配線遅延モデル: hd350s_05k
コンディション: MAX567
クロック CLK (rise edge) 0.00 0.00
CURRENT_reg[1]/D (FD2) 0.00 3.97
データ到着時刻 3.97
クロック CLK (rise edge) 20.00 20.00
クロックスキュー -1.00 19.00
ライブラリのセットアップタイム -0.80 18.20
データ到着要求時刻 18.20
--------------------------------------------
データ到着要求時刻 18.20
データ到着時刻 -3.97
--------------------------------------------
タイミング余裕 14.23
論理合成後、BUTTON, FINOPN, FINCLS → 複数の AND/OR/NOT ゲート → フリップフロップ2個 (FD2) → MOTOR, DIR という回路構造に最適化されます。ステートデコーダを含む比較的コンパクトな回路です(合計30ゲート)。
ワンホット方式 (TRAY_ONEHOT)
****************************************
レポート : area
回路 : TRAY_ONEHOT
****************************************
ポート数: 7
ネット数: 22
セル数: 14
セル種類: 9
組み合わせ回路: 22
非組み合わせ回路: 35
合計: 57
****************************************
レポート : timing
回路 : TRAY_ONEHOT
****************************************
ライブラリ: hd350s
配線遅延モデル: hd350s_05k
コンディション: MAX567
クロック CLK (rise edge) 0.00 0.00
CURRENT_reg[0]/D (FD4) 0.00 3.97
データ到着時刻 3.97
クロック CLK (rise edge) 20.00 20.00
クロックスキュー -1.00 19.00
ライブラリのセットアップタイム -0.80 18.20
データ到着要求時刻 18.20
--------------------------------------------
データ到着要求時刻 18.20
データ到着時刻 -3.97
--------------------------------------------
タイミング余裕 14.23
論理合成後、フリップフロップが4個 (FD4) となり、各ステートに1つのFFが対応します。デコーダが不要になる代わりに組み合わせ回路も増加し、合計57ゲートとなります。タイミング余裕はエンコード方式と同じですが、回路が大きくなるほど速度優位が現れます。
| 項目 | エンコード/デコード方式 | ワンホット方式 |
|---|---|---|
| ポート数 | 7 | 7 |
| ネット数 | 18 | 22 |
| セル数 | 11 | 14 |
| セル種類 | 7 | 9 |
| 組み合わせ回路 | 12 | 22 |
| 非組み合わせ回路(FF) | 18 | 35 |
| 合計 | 30 | 57 |
回路規模の比較
30
57
よく
できました