VHDL で不純な関数を使用する方法
不純な関数は、そのスコープ内の任意のシグナルを読み書きできます。また、パラメーター リストにないシグナルも読み書きできます。関数には副作用があると言います .
副作用とは、関数が同じパラメーターで呼び出されるたびに同じ値を返すことが保証されていないことを意味します。関数がパラメーター リストにないシグナルを読み取ることができる場合、戻り値はこれらのシャドウ パラメーターにも依存する可能性があります。また、関数は戻り値から割り当てられていない外部シグナルを変更している可能性があります。
このブログ投稿は、基本的な VHDL チュートリアル シリーズの一部です。
通常の純粋な関数を宣言できる場所ならどこでも非純粋な関数を宣言できますが、それらをプロセス内で使用することは意味があります。通常シグナルを宣言するアーキテクチャで宣言すると、コンパイル時にどのシグナルもそのスコープに含まれません。したがって、非純粋な関数は、アーキテクチャまたはパッケージ内で宣言されたときに純粋な関数ができる以上のことはできません。
不純な関数を使用する動機は、主にコードの整理です。純粋な関数をパラメーター リストに追加するだけで任意の信号を操作できますが、パラメーター リストが長くなりすぎると、単純化するのではなく難読化してしまいます。
不純な関数を宣言するための構文は、単に impure function
と書くだけです function
の代わりに 宣言するとき。汎用関数の構文については、関数のチュートリアルを参照してください。
エクササイズ
前のチュートリアルでは、時間遅延値を計算する関数を使用して、有限状態マシン (FSM) コードを簡素化しました。各状態の変化を遅らせる時間を指定するために、Minutes および Seconds パラメーターを提供しました。
CounterVal
の場合 関数は true
を返しました 、時間切れになり、次の FSM 状態に移行する時間になりました。同じプロセスで、Counter
もリセットする必要がありました。 そうしないと、関数は次の状態で機能しません。タイマーはすでに期限切れです。
Counter
signal は常に 0
に設定されます 関数が true を返したとき。これが CounterVal
で起こった方がよいのではないでしょうか ステート マシン コードの複数の場所の代わりに機能しますか?
このビデオ チュートリアルでは、不純な関数を使用して、前のチュートリアルの FSM コードを改善します。
不純な関数 testbench の最終的なコード :
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T22_ImpureFunctionTb is end entity; architecture sim of T22_ImpureFunctionTb is -- We are using a low clock frequency to speed up the simulation 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 -- The Device Under Test (DUT) i_TrafficLights : entity work.T22_TrafficLights(rtl) generic map(ClockFrequencyHz => ClockFrequencyHz) port map ( Clk => Clk, nRst => nRst, NorthRed => NorthRed, NorthYellow => NorthYellow, NorthGreen => NorthGreen, WestRed => WestRed, WestYellow => WestYellow, WestGreen => WestGreen); -- Process for generating clock Clk <= not Clk after ClockPeriod / 2; -- Testbench sequence process is begin wait until rising_edge(Clk); wait until rising_edge(Clk); -- Take the DUT out of reset nRst <= '1'; wait; end process; end architecture;
信号機 モジュール の最終的なコード :
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity T22_TrafficLights is generic(ClockFrequencyHz : integer); port( Clk : in std_logic; nRst : in std_logic; -- Negative reset 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 T22_TrafficLights is -- Calculate the number of clock cycles in minutes/seconds function CounterVal(Minutes : integer := 0; Seconds : integer := 0) return integer is variable TotalSeconds : integer; begin TotalSeconds := Seconds + Minutes * 60; return TotalSeconds * ClockFrequencyHz -1; end function; -- Enumerated type declaration and state signal declaration type t_State is (NorthNext, StartNorth, North, StopNorth, WestNext, StartWest, West, StopWest); signal State : t_State; -- Counter for counting clock periods, 1 minute max signal Counter : integer range 0 to ClockFrequencyHz * 60; begin process(Clk) is -- This impure function reads and drives the Counter signal -- which is not on the parameter list. impure function CounterExpired(Minutes : integer := 0; Seconds : integer := 0) return boolean is begin if Counter = CounterVal(Minutes, Seconds) then Counter <= 0; return true; else return false; end if; end function; begin if rising_edge(Clk) then if nRst = '0' then -- Reset values State <= NorthNext; Counter <= 0; NorthRed <= '1'; NorthYellow <= '0'; NorthGreen <= '0'; WestRed <= '1'; WestYellow <= '0'; WestGreen <= '0'; else -- Default values NorthRed <= '0'; NorthYellow <= '0'; NorthGreen <= '0'; WestRed <= '0'; WestYellow <= '0'; WestGreen <= '0'; Counter <= Counter + 1; case State is -- Red in all directions when NorthNext => NorthRed <= '1'; WestRed <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= StartNorth; end if; -- Red and yellow in north/south direction when StartNorth => NorthRed <= '1'; NorthYellow <= '1'; WestRed <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= North; end if; -- Green in north/south direction when North => NorthGreen <= '1'; WestRed <= '1'; -- If 1 minute has passed if CounterExpired(Minutes => 1) then State <= StopNorth; end if; -- Yellow in north/south direction when StopNorth => NorthYellow <= '1'; WestRed <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= WestNext; end if; -- Red in all directions when WestNext => NorthRed <= '1'; WestRed <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= StartWest; end if; -- Red and yellow in west/east direction when StartWest => NorthRed <= '1'; WestRed <= '1'; WestYellow <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= West; end if; -- Green in west/east direction when West => NorthRed <= '1'; WestGreen <= '1'; -- If 1 minute has passed if CounterExpired(Minutes => 1) then State <= StopWest; end if; -- Yellow in west/east direction when StopWest => NorthRed <= '1'; WestYellow <= '1'; -- If 5 seconds have passed if CounterExpired(Seconds => 5) then State <= NorthNext; end if; end case; end if; end if; end process; end architecture;
run 5 min
に入った後の波形 ModelSim コンソールのコマンド:
分析
波形からわかるように、モジュールの出力は、不純な関数を追加した後も変化していません。ロジックはまったく変更しておらず、コードだけを変更しています。
Counter
の評価 シグナルは FSM コードから新しい不純な関数 CounterExpired
に移動されました . Counter <= 0;
Counter
をクリアするための行 signal も不純関数に移動されました。
その結果、より簡単に保守できる FSM コードが読みやすくなります。これは主観的なものですが、私にとっては CounterExpired(Seconds => 5)
Counter = CounterVal(Seconds => 5)
よりも目にやさしい .
不純な関数をどこまで使用するかは、完全にあなたとあなたのサービスにお金を払う人次第です。一部の人々は、サブプログラムに隠されているアルゴリズムのすべての原因と結果を見抜くのが難しいため、注意して使用する必要があると感じています。私と同じように、意図を明確にすれば、コードが読みやすくなり、実際にはエラーが発生しにくくなると感じる人もいます。
このため、製品モジュールよりもテストベンチ コードで不純な関数を見つける可能性が高くなります。通常、テストベンチはテスト対象のモジュールよりも複雑であり、コードの正確性に対する要件は RTL コードほど厳密ではありません。
テイクアウト
- 不純な関数は、パラメータ リストにない信号を読み取ったり駆動したりできます
- プロセス内で純粋でない関数を宣言することだけが理にかなっています
次のチュートリアルに進む »
VHDL