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