プロセス内プロシージャでVHDLステートマシンをクリーンに設計
VHDLでは、プロシージャがスコープ内にある限り外部信号を駆動できます。アーキテクチャで宣言されたプロシージャは外部信号にアクセスできません(コンパイル時にそれらの信号が見えないため)が、プロセス内で定義されたプロシージャは、そのプロセスが見えるすべての信号を自由に読み書きできます。
プロセス内プロシージャを使用することは、ステートマシンや複雑なプロトコルにおける繰り返しロジックを整理する効果的な方法です。共通アクション(タイムド状態遷移など)をプロシージャにカプセル化することで、ボイラープレートコードを削減し、可読性を向上させ、各ステップの意図を明確にできます。
例えば、信号機コントローラでChangeState(ToState, Minutes, Seconds)のように操作名を変更すれば、基礎となる変数代入を深く見なくてもコードが何をしているか一目でわかります。
実践演習
前回のチュートリアルでは、不純関数を用いてCounter信号を駆動し、状態遷移を判定しました。しかし、VHDLでは関数呼び出しに戻り値が必要なため、State代入をその関数に移動できませんでした。代わりに、戻り値を持たないプロシージャがこのユースケースに最適です。
以下は、プロセス内プロシージャで駆動される信号機FSMの完全なテストベンチとモジュールです。
テストベンチ
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity T23_ProcedureInProcessTb is
end entity;
architecture sim of T23_ProcedureInProcessTb is
constant ClockFrequencyHz : integer := 100; -- 100 Hz
constant ClockPeriod : time := 1000 ms / ClockFrequencyHz;
signal Clk : std_logic := '1';
signal nRst : std_logic := '0';
signal NorthRed : std_logic;
signal NorthYellow : std_logic;
signal NorthGreen : std_logic;
signal WestRed : std_logic;
signal WestYellow : std_logic;
signal WestGreen : std_logic;
begin
i_TrafficLights : entity work.T23_TrafficLights(rtl)
generic map(ClockFrequencyHz => ClockFrequencyHz)
port map (
Clk => Clk,
nRst => nRst,
NorthRed => NorthRed,
NorthYellow => NorthYellow,
NorthGreen => NorthGreen,
WestRed => WestRed,
WestYellow => WestYellow,
WestGreen => WestGreen
);
Clk <= not Clk after ClockPeriod / 2;
process is
begin
wait until rising_edge(Clk);
wait until rising_edge(Clk);
nRst <= '1';
wait;
end process;
end architecture;
信号機モジュール
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity T23_TrafficLights is
generic(ClockFrequencyHz : integer);
port(
Clk : in std_logic;
nRst : in std_logic;
NorthRed : out std_logic;
NorthYellow : out std_logic;
NorthGreen : out std_logic;
WestRed : out std_logic;
WestYellow : out std_logic;
WestGreen : out std_logic
);
end entity;
architecture rtl of T23_TrafficLights is
type t_State is (NorthNext, StartNorth, North, StopNorth, WestNext, StartWest, West, StopWest);
signal State : t_State;
signal Counter : integer range 0 to ClockFrequencyHz * 60;
begin
process(Clk) is
procedure ChangeState(ToState : t_State; Minutes : integer := 0; Seconds : integer := 0) is
variable TotalSeconds : integer;
variable ClockCycles : integer;
begin
TotalSeconds := Seconds + Minutes * 60;
ClockCycles := TotalSeconds * ClockFrequencyHz - 1;
if Counter = ClockCycles then
Counter <= 0;
State <= ToState;
end if;
end procedure;
begin
if rising_edge(Clk) then
if nRst = '0' then
State <= NorthNext;
Counter <= 0;
NorthRed <= '1'; NorthYellow <= '0'; NorthGreen <= '0';
WestRed <= '1'; WestYellow <= '0'; WestGreen <= '0';
else
NorthRed <= '0'; NorthYellow <= '0'; NorthGreen <= '0';
WestRed <= '0'; WestYellow <= '0'; WestGreen <= '0';
Counter <= Counter + 1;
case State is
when NorthNext =>
NorthRed <= '1'; WestRed <= '1';
ChangeState(StartNorth, Seconds => 5);
when StartNorth =>
NorthRed <= '1'; NorthYellow <= '1'; WestRed <= '1';
ChangeState(North, Seconds => 5);
when North =>
NorthGreen <= '1'; WestRed <= '1';
ChangeState(StopNorth, Minutes => 1);
when StopNorth =>
NorthYellow <= '1'; WestRed <= '1';
ChangeState(WestNext, Seconds => 5);
when WestNext =>
NorthRed <= '1'; WestRed <= '1';
ChangeState(StartWest, Seconds => 5);
when StartWest =>
NorthRed <= '1'; WestRed <= '1'; WestYellow <= '1';
ChangeState(West, Seconds => 5);
when West =>
NorthRed <= '1'; WestGreen <= '1';
ChangeState(StopWest, Minutes => 1);
when StopWest =>
NorthRed <= '1'; WestYellow <= '1';
ChangeState(NorthNext, Seconds => 5);
end case;
end if;
end if;
end process;
end architecture;
波形
シミュレーション結果は元のFSMと同一です。コードのみがより保守しやすく、理解しやすくなりました。
分析
タイミングと状態遷移ロジックを単一プロシージャに移動することで、モジュールの動作を維持しながら可読性を大幅に向上させました。プロシージャにより、呼び出し箇所どこでも一貫した実装が保証されます。
重要なポイント
- プロセス内のプロシージャは、そのプロセスから見えるすべての信号にアクセスできる。
- 繰り返しロジックをプロシージャにカプセル化することで、コードの明確さと保守性が向上する。
VHDL
- カッターグラインダー技術の習得:実践ガイド
- モリブデンが現代産業を形作る仕組み:用途、利点、需給動向
- VHDL入門:最初のANDゲートを作る
- VHDLプロシージャ例:std_logic_vectorのインクリメント
- VHDLで文字列リストを作成する: ベスト・プラクティスと実装例
- VHDLにおける不純関数の活用:FSMの可読性と保守性向上
- VHDL関数マスター:効率的な設計のための実践ガイド
- VHDLプロシージャ活用:再利用可能なコードで設計をシンプルに
- VHDLで信頼性の高いタイマーを構築:クロックサイクルカウントで時間を計測
- VHDLでクロック駆動プロセスを構築:実践ガイド
- VHDLポートマップインスタンス化マスター:実践ガイド