読み込み中...

VHDLステートマシン完全解説!10選の実用サンプルコード

VHDLステートマシンの実用サンプルコードを詳しく解説するイメージ VHDL
この記事は約30分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラム(回路記述)の基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

VHDLでのステートマシンの実装は、デジタル回路設計における中核的な技術の一つです。

本記事では、VHDLにおけるステートマシンの基礎から応用、さらにはカスタマイズの方法までを初心者目線でわかりやすく解説します。

実用的な10のサンプルコードとともに、ステートマシンの魅力とその応用例を深掘りします。

●VHDLとステートマシンの基本

○VHDLの概要

VHDLは、デジタルシステムの設計とシミュレーションのためのハードウェア記述言語です。

デジタル回路の振る舞いをテキストで記述し、それを基にハードウェアの動作をシミュレートしたり、FPGAやASICへの実装を行うことができます。

○ステートマシンの原理

ステートマシンは、特定の「状態」の集合と、それらの状態間を移動するための「遷移条件」を持つ構造です。

一度ある状態に遷移すると、次の状態へ移るまでその状態に留まります。

ステートマシンは、入力に応じて状態を遷移させ、その状態に応じて出力を変更することで動作します。

●VHDLでのステートマシンの作り方

○基本構造とは

VHDLでのステートマシンは、enum型を使用して状態を定義し、case文やif文を使用して状態の遷移や出力の制御を行います。

○サンプルコード1:基本的なステートマシン

このコードでは、2つの状態STATE_AとSTATE_Bを持つ基本的なステートマシンを表しています。

この例では、初期状態をSTATE_Aとし、クロックの立ち上がりエッジごとに状態が切り替わる動作をしています。

type state_type is (STATE_A, STATE_B);
signal current_state : state_type := STATE_A;
signal next_state : state_type;

process(clock)
begin
  if rising_edge(clock) then
    current_state <= next_state;
  end if;
end process;

process(current_state)
begin
  case current_state is
    when STATE_A =>
      next_state <= STATE_B;
    when STATE_B =>
      next_state <= STATE_A;
  end case;
end process;

このサンプルコードを動かすと、毎回のクロックの立ち上がりでcurrent_stateがSTATE_AからSTATE_B、またはその逆に切り替わります。

○サンプルコード2:複数のステートを持つ例

このコードでは、3つの状態STATE_A, STATE_B, STATE_Cを使って、順番に状態が遷移するステートマシンを表しています。

この例では、クロックの立ち上がりエッジで状態が順番に切り替わる動作をしています。

type state_type is (STATE_A, STATE_B, STATE_C);
signal current_state : state_type := STATE_A;
signal next_state : state_type;

process(clock)
begin
  if rising_edge(clock) then
    current_state <= next_state;
  end if;
end process;

process(current_state)
begin
  case current_state is
    when STATE_A =>
      next_state <= STATE_B;
    when STATE_B =>
      next_state <= STATE_C;
    when STATE_C =>
      next_state <= STATE_A;
  end case;
end process;

このサンプルコードを実行すると、毎回のクロックの立ち上がりでcurrent_stateがSTATE_A, STATE_B, STATE_Cと順番に切り替わります。

●ステートマシンの応用例

ステートマシンは単純な動作だけでなく、複雑な制御や動作の組み合わせにも使用されます。

ここでは、VHDLでのステートマシンの応用例について詳しく解説します。

○サンプルコード3:条件分岐を取り入れた例

このコードではVHDLで条件分岐を取り入れたステートマシンを紹介しています。

この例では、入力の値に応じて異なるステートに遷移するよう設計されています。

entity condition_state_machine is
    Port ( clk : in STD_LOGIC;
           rst : in STD_LOGIC;
           inp : in STD_LOGIC_VECTOR(1 downto 0);
           outp : out STD_LOGIC_VECTOR(1 downto 0));
end condition_state_machine;

architecture Behavioral of condition_state_machine is
    type state_type is (A, B, C);
    signal current_state, next_state : state_type;
begin
    process(clk, rst)
    begin
        if rst = '1' then
            current_state <= A;
        elsif rising_edge(clk) then
            current_state <= next_state;
        end if;
    end process;

    process(current_state, inp)
    begin
        case current_state is
            when A =>
                if inp = "00" then
                    next_state <= B;
                else
                    next_state <= C;
                end if;
                outp <= "01";

            when B =>
                next_state <= A;
                outp <= "10";

            when C =>
                next_state <= B;
                outp <= "11";
        end case;
    end process;
end Behavioral;

上記のコードでは、inpの値が”00″の場合、AステートからBステートに遷移します。

それ以外の場合は、AステートからCステートに遷移します。BステートとCステートはそれぞれ固定で次のステートに遷移します。

もしこのコードをFPGAやシミュレータで実行すると、入力のinpに応じて出力のoutpが変わることが確認できます。例えば、inpが”00″のとき、outpは”01″になります。

○サンプルコード4:外部入力を取り扱う例

次に、外部入力を用いたステートマシンを紹介します。

この例では、外部からの入力信号に基づいてステート遷移を制御することを目的としています。

entity external_input_sm is
    Port ( clk : in STD_LOGIC;
           rst : in STD_LOGIC;
           external_inp : in STD_LOGIC;
           outp : out STD_LOGIC);
end external_input_sm;

architecture Behavioral of external_input_sm is
    type state_type is (X, Y);
    signal current_state, next_state : state_type;
begin
    process(clk, rst)
    begin
        if rst = '1' then
            current_state <= X;
        elsif rising_edge(clk) then
            current_state <= next_state;
        end if;
    end process;

    process(current_state, external_inp)
    begin
        case current_state is
            when X =>
                if external_inp = '1' then
                    next_state <= Y;
                else
                    next_state <= X;
                end if;
                outp <= '0';

            when Y =>
                next_state <= X;
                outp <= '1';
        end case;
    end process;
end Behavioral;

このコードでは、外部入力external_inpが’1’の場合にステートXからステートYに遷移します。ステートYでは、常にステートXに戻ります。

この動作を実際に確認すると、external_inpが’1’になるたびに、outpが一度’1’になり、その後’0’に戻ることがわかります。

○サンプルコード5:タイマー機能を持つステートマシン

ステートマシンは非常に多様な用途で活用できるため、特定の目的に合わせてカスタマイズすることが重要となります。

今回は、時間経過に応じて状態遷移を行う、タイマー機能を持つステートマシンの作成方法について詳しく説明します。

このコードでは、VHDLを使用して時間経過に応じてステートが遷移するタイマー機能を持つステートマシンを実装しています。

この例では、定義した時間が経過すると次のステートへ遷移し、全てのステートの遷移が完了したら初期ステートに戻る仕組みを取り入れています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity TimerStateMachine is
    Port ( clk : in STD_LOGIC;
           rst : in STD_LOGIC;
           state_out : out STD_LOGIC_VECTOR(1 downto 0));
end TimerStateMachine;

architecture Behavioral of TimerStateMachine is
    type state_type is (s0, s1, s2);
    signal current_state, next_state: state_type;
    signal counter: integer range 0 to 100 := 0; -- カウンターの設定
begin
    process(clk, rst)
    begin
        if rst = '1' then
            current_state <= s0;
            counter <= 0;
        elsif rising_edge(clk) then
            current_state <= next_state;
            if counter < 100 then
                counter <= counter + 1;
            else
                counter <= 0;
            end if;
        end if;
    end process;

    process(current_state, counter)
    begin
        case current_state is
            when s0 =>
                if counter = 100 then -- 100クロック後に遷移
                    next_state <= s1;
                else
                    next_state <= s0;
                end if;
            when s1 =>
                if counter = 100 then
                    next_state <= s2;
                else
                    next_state <= s1;
                end if;
            when s2 =>
                if counter = 100 then
                    next_state <= s0;
                else
                    next_state <= s2;
                end if;
        end case;
    end process;

    state_out <= "00" when current_state = s0 else
                 "01" when current_state = s1 else
                 "10";
end Behavioral;

このサンプルコードでは、3つのステート(s0, s1, s2)を持ち、それぞれのステートで100クロック待機してから次のステートへ遷移する動作を行います。

これにより、一定の時間を計測するタイマーとして機能するステートマシンが構築できます。

また、最後のステートs2からは再びs0へと戻るので、繰り返し動作します。

このタイマーステートマシンは、例えばLEDの点滅や特定の時間ごとの信号処理など、時間制御が必要な場面で役立ちます。

VHDLのステートマシンを利用することで、硬定義のタイマー制御を簡単に実現することが可能です。

実際に上記のコードをFPGAなどのハードウェアに組み込み実行すると、指定したクロック周期でステートが遷移する様子を確認することができます。

具体的には、state_outの出力が”00″→”01″→”10″の順に変わり、そして”00″に戻る、という動作を繰り返します。

これは、各ステートで設定した100クロックの時間が経過するたびに次のステートに移行していることを表しています。

●詳細な注意点

VHDLのステートマシンを実装する際、特に初心者の方がよく犯すミスや誤解するポイントが存在します。

それらのミスを避けるための知識と、具体的なサンプルコードを用いての注意点をここでは紹介します。

○初心者が陥りやすいミス

❶ステート間の遷移が不完全

ステート間の遷移が明確に定義されていない場合、予期しない動作をする可能性があります。

❷ステートの重複

同じ機能を持つステートが複数存在する場合、コードの保守性や読みやすさが低下します。

❸未使用のステート

実際の動作には不要なステートが定義されている場合、無駄なリソースを消費する原因となります。

❹初期ステートの不明確さ

ステートマシンの動作開始時のステートが明確でないと、期待する動作をしない可能性が高まります。

○サンプルコード6:注意点を考慮した安全なステートマシン

このコードではVHDLを使ってステートマシンの注意点を考慮して安全に動作する例を表しています。

この例では初心者がよく犯すミスを避け、各ステートの遷移を明確に表しています。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity safe_state_machine is
    Port ( clk : in STD_LOGIC;
           rst : in STD_LOGIC;
           start : in STD_LOGIC;
           done : out STD_LOGIC);
end safe_state_machine;

architecture Behavior of safe_state_machine is
    -- ステートの定義
    type state_type is (IDLE, PROCESS, END);
    signal current_state, next_state: state_type;

begin
    process(clk, rst)
    begin
        -- リセット時の処理
        if rst = '1' then
            current_state <= IDLE;
        elsif rising_edge(clk) then
            current_state <= next_state;
        end if;
    end process;

    process(current_state, start)
    begin
        case current_state is
            when IDLE =>
                done <= '0';
                if start = '1' then
                    next_state <= PROCESS;
                else
                    next_state <= IDLE;
                end if;
            when PROCESS =>
                -- 何らかの処理
                next_state <= END;
            when END =>
                done <= '1';
                next_state <= IDLE;
        end case;
    end process;
end Behavior;

上記のサンプルコードでは、ステートマシンの初期ステートをIDLEと明確に指定しています。

また、各ステートの遷移も明確に記述しているため、期待した動作を安定して行うことができます。

特に、リセット信号rstがアクティブになった場合、ステートマシンはIDLEステートに強制的に遷移するように設計されています。

このコードを実行すると、start信号がアクティブになると、ステートマシンはIDLEステートからPROCESSステートへと遷移します。

そして、何らかの処理が完了した後に、ENDステートへと遷移し、最終的にdone信号をアクティブにします。

この動作が終了すると再びIDLEステートに戻ります。

VHDLでのステートマシンの設計には、上述のような注意点を常に心掛けることが重要です。

特に初心者の方は、適切な初期ステートの設定や、全てのステートでの遷移処理をしっかりと記述することで、思わぬトラブルを避けることができます。

●カスタマイズの方法

VHDLステートマシンの設計は、基本的な動作から高度な動作までさまざまなカスタマイズが可能です。

ここでは、VHDLステートマシンのカスタマイズ例を3つのサンプルコードとともに紹介します。

○サンプルコード7:カスタマイズ例1

このコードでは、外部からの入力に応じてステートを変更する方法を紹介しています。

この例では、外部スイッチの入力を読み取り、その入力値に応じて異なる動作をするステートマシンを実現しています。

entity custom_state_machine is
    Port ( clk : in STD_LOGIC;
           reset : in STD_LOGIC;
           switch_input : in STD_LOGIC_VECTOR(1 downto 0);
           led_output : out STD_LOGIC_VECTOR(1 downto 0));
end custom_state_machine;

architecture Behavioral of custom_state_machine is
    type state_type is (STATE_A, STATE_B, STATE_C);
    signal current_state, next_state : state_type;
begin
    process(clk, reset)
    begin
        if reset = '1' then
            current_state <= STATE_A;
        elsif rising_edge(clk) then
            current_state <= next_state;
        end if;
    end process;

    process(current_state, switch_input)
    begin
        case current_state is
            when STATE_A =>
                if switch_input = "00" then
                    next_state <= STATE_B;
                    led_output <= "01";
                else
                    next_state <= STATE_A;
                    led_output <= "00";
                end if;
            when STATE_B =>
                if switch_input = "01" then
                    next_state <= STATE_C;
                    led_output <= "10";
                else
                    next_state <= STATE_B;
                    led_output <= "01";
                end if;
            when STATE_C =>
                next_state <= STATE_A;
                led_output <= "11";
        end case;
    end process;
end Behavioral;

このサンプルコードは、スイッチの入力値によってLEDの出力を変更します。

具体的には、スイッチの入力が”00″の場合はLEDの出力が”01″に、”01″の場合は”10″に、それ以外の場合は”11″になります。

○サンプルコード8:カスタマイズ例2

このコードでは、VHDLステートマシンに時間遅延機能を追加する方法を表しています。

この例では、特定のステートに遷移した後、一定のクロックサイクル後に次のステートに遷移する機能を実現しています。

-- こちらのコードは、時間遅延機能を持つステートマシンの例です。
-- 下記の実装では、STATE_DELAYステートに入ると、5クロックサイクル後にSTATE_NEXTに遷移します。

-- エンティティの定義
entity delay_state_machine is
    Port ( clk : in STD_LOGIC;
           reset : in STD_LOGIC;
           led_output : out STD_LOGIC);
end delay_state_machine;

-- アーキテクチャの定義
architecture Behavioral of delay_state_machine is
    type state_type is (STATE_INIT, STATE_DELAY, STATE_NEXT);
    signal current_state, next_state : state_type;
    signal counter : integer range 0 to 5 := 0;
begin
    -- ステート遷移のプロセス
    process(clk, reset)
    begin
        if reset = '1' then
            current_state <= STATE_INIT;
        elsif rising_edge(clk) then
            current_state <= next_state;
        end if;
    end process;

    -- ステートマシンの動作を定義するプロセス
    process(current_state)
    begin
        case current_state is
            when STATE_INIT =>
                next_state <= STATE_DELAY;
                led_output <= '0';
            when STATE_DELAY =>
                if counter = 5 then
                    next_state <= STATE_NEXT;
                    led_output <= '1';
                else
                    next_state <= STATE_DELAY;
                end if;
            when STATE_NEXT =>
                next_state <= STATE_INIT;
                led_output <= '0';
        end case;
    end process;

    -- カウンタのプロセス
    process(clk, reset)
    begin
        if reset = '1' then
            counter <= 0;
        elsif rising_edge(clk) and current_state = STATE_DELAY then
            if counter < 5 then
                counter <= counter + 1;
            end if;
        end if;
    end process;
end Behavioral;

このサンプルコードは、STATE_DELAYステートに遷移すると、5クロックサイクル後にSTATE_NEXTステートに遷移する機能を持ちます。

この機能を利用して、特定の動作を一時停止させたり、特定の期間だけ特定の出力を持続させることができます。

○サンプルコード9:カスタマイズ例3

このコードでは、外部からの複数の入力を同時に処理するステートマシンの作成方法を表しています。

この例では、2つのスイッチ入力を同時に読み取り、それらの組み合わせに応じて異なる動作をするステートマシンを実現しています。

-- このコードでは、2つのスイッチ入力を同時に処理するステートマシンの作成方法を表しています。

entity multi_input_state_machine is
    Port ( clk : in STD_LOGIC;
           reset : in STD_LOGIC;
           switch_input1, switch_input2 : in STD_LOGIC;
           led_output : out STD_LOGIC_VECTOR(1 downto 0));
end multi_input_state_machine;

architecture Behavioral of multi_input_state_machine is
    type state_type is (STATE_1, STATE_2, STATE_3, STATE_4);
    signal current_state, next_state : state_type;
begin
    process(clk, reset)
    begin
        if reset = '1' then
            current_state <= STATE_1;
        elsif rising_edge(clk) then
            current_state <= next_state;
        end if;
    end process;

    process(current_state, switch_input1, switch_input2)
    begin
        case current_state is
            when STATE_1 =>
                if switch_input1 = '1' and switch_input2 = '0' then
                    next_state <= STATE_2;
                    led_output <= "01";
                else
                    next_state <= STATE_1;
                    led_output <= "00";
                end if;
            when STATE_2 =>
                if switch_input1 = '0' and switch_input2 = '1' then
                    next_state <= STATE_3;
                    led_output <= "10";
                else
                    next_state <= STATE_2;
                    led_output <= "01";
                end if;
            when STATE_3 =>
                if switch_input1 = '1' and switch_input2 = '1' then
                    next_state <= STATE_4;
                    led_output <= "11";
                else
                    next_state <= STATE_3;
                    led_output <= "10";
                end if;
            when STATE_4 =>
                next_state <= STATE_1;
                led_output <= "00";
        end case;
    end process;
end Behavioral;

このサンプルコードは、2つのスイッチ入力の組み合わせによって、LEDの出力を変更します。

具体的には、スイッチ1がON、スイッチ2がOFFの場合はLEDの出力が”01″に、スイッチ1がOFF、スイッチ2がONの場合は”10″に、両方のスイッチがONの場合は”11″になります。

●VHDLのステートマシンをさらに深掘り

VHDLでのステートマシンの設計は、デジタルロジック設計において中核を成す部分です。

ステートマシンは、一連の状態を持つシステムで、各状態間を遷移することで動作します。

この遷移は、外部入力や現在の状態によって決定されます。

例えば、自動販売機や信号機などの実際のシステムは、ステートマシンを用いて設計されています。

これらのシステムは、特定の条件下で特定の出力を生成する必要があります。

VHDLを使用すると、これらの複雑なシステムの動作をシミュレートして、正しく動作することを確認する前に設計することができます。こ

の段階での検証は、実際のハードウェアを作成する前に、多くの時間とコストを節約するのに役立ちます。

○サンプルコード10:高度な機能を取り入れたステートマシン

このコードでは、高度な機能を持つステートマシンを実装しています。

この例では、外部からの複数の入力信号に基づいて、特定の出力を動的に生成する方法を表しています。

entity advanced_state_machine is
    Port ( clk : in STD_LOGIC;
           reset : in STD_LOGIC;
           input1, input2 : in STD_LOGIC_VECTOR(2 downto 0);
           output_signal : out STD_LOGIC_VECTOR(2 downto 0));
end advanced_state_machine;

architecture Behavioral of advanced_state_machine is
    type state_type is (STATE_A, STATE_B, STATE_C, STATE_D);
    signal current_state, next_state : state_type;
begin
    process(clk, reset)
    begin
        if reset = '1' then
            current_state <= STATE_A;
        elsif rising_edge(clk) then
            current_state <= next_state;
        end if;
    end process;

    process(current_state, input1, input2)
    begin
        case current_state is
            when STATE_A =>
                if input1 = "001" and input2 = "010" then
                    next_state <= STATE_B;
                    output_signal <= "110";
                else
                    next_state <= STATE_A;
                    output_signal <= "000";
                end if;
            when STATE_B =>
                if input1 = "011" and input2 = "100" then
                    next_state <= STATE_C;
                    output_signal <= "101";
                else
                    next_state <= STATE_B;
                    output_signal <= "001";
                end if;
            -- 以下、STATE_CとSTATE_Dの処理も同様
        end case;
    end process;
end Behavioral;

上記のコードは、input1input2の2つの3ビット入力信号をもとに、出力信号output_signalの状態を動的に変更します。

特定の入力条件下で、出力は"110"または"101"のような異なる値に変わります。

この例に基づいて、input1が”001″、input2が”010″のとき、出力は”110″になります。

同様に、input1が”011″、input2が”100″のとき、出力は”101″に変わります。

まとめ

VHDLでのステートマシン設計は、デジタルロジック設計の中心的な要素です。

この記事では、VHDLを用いたステートマシンの基本から応用までを、10の具体的なサンプルコードとともに紹介しました。

これらのサンプルコードは、実際のハードウェア設計の際の参考として役立つでしょう。

VHDLのステートマシンを深く理解することで、より複雑で効率的なデジタルロジックシステムの設計が可能になります。