FPGAのロジックをVHDL(やverilog-HDL)で設計していると、どのようなコーディングスタイルを採用するか悩むことがあります。(設計人口も公開された情報も格段に少ないので、C言語やpythonといったプログラミング言語よりも悩みは深いかもしれません)
ステートマシンの記述法
VHDLやverilog-HDLでステートマシン(Finite State Machine:FSM)を記述する場合、case文で記述することが一般的とされていますがif文で記述することもできます。
(入門書でもネットで検索しても、ほぼcase文の例しか出てこないので、多くの人はif文による記述を試したことがないかもしれません)
if文とcase文それぞれの記述例を下記に示します。どちらも等価な動作をするよう意図しています。しかし、必ずしも同じロジックが合成されるわけではありません。
if文記述例では、else節の記述により、それまでのif-elsifで記述された条件以外の全ての条件でIDLE状態に戻すようにしています。この場合、signal ST0 に定義された状態だけでなく全ての条件が対象になります。
case文記述例では、when ohters節がありますが、この例ではすでにsignal ST0 に定義された3状態を記述しているのでwhen ohters節は無視されます(合成/シミュレーションツールの設定によってはエラーや警告が出る場合もあります)。
library IEEE;
use IEEE.std_logic_1164.all;
entity FSM is
port(
RST_I_N : in std_logic;
CLK_I : in std_logic;
RUN_I : in std_logic;
IDLE_O : out std_logic;
ST_O : out std_logic;
BSY_O : out std_logic
);
end FSM;
architecture RTL of FSM is
type T_ST is (
IDLE
,ST
,BSY
);
signal ST0 : T_ST;
begin
P_ST: process(RST_I_N, CLK_I)
begin
if (RST_I_N = '0') then
ST0 <= IDLE;
elsif (rising_edge(CLK_I)) then
if (ST0 = IDLE) then
if (RUN_I = '1') then
ST0 <= ST;
end if;
elsif (ST0 = ST) then
ST0 <= BSY;
elsif (ST0 = BSY) then
if (RUN_I /= '1') then
ST0 <= IDLE;
end if;
else
ST0 <= IDLE;
end if;
end if;
end process;
IDLE_O <= '1' when (ST0 = IDLE) else '0';
ST_O <= '1' when (ST0 = ST) else '0';
BSY_O <= '1' when (ST0 = BSY) else '0';
end RTL;
library IEEE;
use IEEE.std_logic_1164.all;
entity FSM is
port(
RST_I_N : in std_logic;
CLK_I : in std_logic;
RUN_I : in std_logic;
IDLE_O : out std_logic;
ST_O : out std_logic;
BSY_O : out std_logic
);
end FSM;
architecture RTL of FSM is
type T_ST is (
IDLE
,ST
,BSY
);
signal ST0 : T_ST;
begin
P_ST: process(RST_I_N, CLK_I)
begin
if (RST_I_N = '0') then
ST0 <= IDLE;
elsif (rising_edge(CLK_I)) then
case ST0 is
when IDLE =>
if (RUN_I = '1') then
ST0 <= ST;
end if;
when ST =>
ST0 <= BSY;
when BSY =>
if (RUN_I /= '1') then
ST0 <= IDLE;
end if;
when others => -- この節は無視される
ST0 <= IDLE;
end case;
end if;
end process;
IDLE_O <= '1' when (ST0 = IDLE) else '0';
ST_O <= '1' when (ST0 = ST) else '0';
BSY_O <= '1' when (ST0 = BSY) else '0';
end RTL;
合成・シミュレーションしてみる
これらのステートマシン記述を実際のFPGAに適用するとどうなるでしょうか。
IntelのMAX10をターゲットデバイスとして、Quartus Primeで論理合成し、その結果(.vhoファイル)に対してQuestaでシミュレーションしてみました。
Quartus Prime Version ; 22.1std.2 Build 922 07/20/2023 SC Lite Edition
Family ; MAX 10
Device ; 10M02SCE144C8G
Questa Intel Starter FPGA Edition-64 Version 2021.2 win64 Apr 14 2021
多くのFPGA合成ツールでそうであるように、Quartus Primeでもデフォルト設定ではステートマシンエンコーディングはAutoとなっており、結果としてOne-Hotエンコーディングになります。
Encoding Type: One-Hot
+----------------------------------------+
; State Machine - |FSM|ST0 ;
+----------+---------+--------+----------+
; Name ; ST0.BSY ; ST0.ST ; ST0.IDLE ;
+----------+---------+--------+----------+
; ST0.IDLE ; 0 ; 0 ; 0 ;
; ST0.ST ; 0 ; 1 ; 1 ;
; ST0.BSY ; 1 ; 0 ; 1 ;
+----------+---------+--------+----------+
今回は3状態のステートマシンなので3ビットで構成されています。3ビットのレジスターは 23=8 状態を取りうるので、定義された3状態以外の5状態は未定義状態ということになります。
Single Event Upset(SEU)のようなソフトエラーが発生した場合にこれらの未定義状態に陥る場合がありますので、そこから自動復帰できるか否かが信頼性を決める重要な要素になります。
今回は、未定義状態のシミュレーションのため、Questaの実行時に
run 150 ns
force -deposit sim:/tb/U0/\\ST0.BSY~q\\ 0 0
run 150 ns
として、BSYステート中、150nsの時点でST0.BSY~qを強制的に0にしてみました。
if文記述の場合
150ns時点でST0.BSY~qが0になりすべての出力が0になりましたが、次のクロックエッジで(else節に救われて)IDLEステートに復帰し、それ以降は正常動作に戻りました。
case文記述の場合
150ns時点でST0.BSY~qが0になりすべての出力が0になり、その後復帰することはありませんでした。
case文記述で Safe State Machine = ON の場合
Quartus Primeには(Quartus IIの時代から)Safe State Machineというオプションがあり、デフォルトではオフになっています。これをオンにすると、case文記述でも、未定義状態からの自動復帰ができるようになります。
Encoding Type: Safe One-Hot
+----------------------------------------+
; State Machine - |FSM|ST0 ;
+----------+---------+--------+----------+
; Name ; ST0.BSY ; ST0.ST ; ST0.IDLE ;
+----------+---------+--------+----------+
; ST0.IDLE ; 0 ; 0 ; 0 ;
; ST0.ST ; 0 ; 1 ; 1 ;
; ST0.BSY ; 1 ; 0 ; 1 ;
+----------+---------+--------+----------+
この場合、if文記述の場合と同様な動作となり、
150ns時点でST0.BSY~qが0になりすべての出力が0になりましたが、次のクロックエッジでIDLEステートに復帰し、それ以降は正常動作に戻りました。
まとめ
これらのシミュレーション結果に、配置配線後の回路規模(logic elements)と動作周波数(Fmax)を加えて表にまとめてみました。
if文記述 | case文記述 | case文記述(Safe State Machine) | |
---|---|---|---|
Total logic elements | 4 | 4 | 4 |
Fmax (Slow 1200mV 85C Model) [MHz] | 549.15 | 1018.33 | 618.81 |
未定義状態からの復帰 | OK | NG | OK |
回路規模は、今回のように小さいステートマシンではどの記述方法でもほとんど変わらないことがわかります。
動作周波数は、case文記述のほうが有利なようですが、if文記述でもこのデバイスのRestricted Fmax (250MHz)を大きく超えているので、実際には問題ありません。通常は、よほど巨大なひどいステートマシンを作らない限り、ステートマシンがボトルネックになることはないでしょう。
未定義状態からの復帰については、if文記述のほうが良いということがわかりました。case文記述で Safe State MachineオプションをONにしても良いですが、どのような設定でも安全なステートマシン作れるif文記述のほうがより良いといえるでしょう。