So verwenden Sie eine unreine Funktion in VHDL
Eine unreine Funktion kann jedes Signal innerhalb ihres Gültigkeitsbereichs lesen oder schreiben, auch solche, die nicht auf der Parameterliste stehen. Wir sagen, dass die Funktion Nebenwirkungen hat .
Was wir mit Nebeneffekten meinen, ist, dass nicht garantiert ist, dass die Funktion bei jedem Aufruf mit denselben Parametern denselben Wert zurückgibt. Wenn die Funktion Signale lesen kann, die nicht auf der Parameterliste stehen, kann der Rückgabewert auch von diesen Schattenparametern abhängen. Außerdem kann die Funktion externe Signale ändern, die nicht von ihrem Rückgabewert zugewiesen sind.
Dieser Blogpost ist Teil der Reihe Basic VHDL Tutorials.
Obwohl wir unreine Funktionen überall dort deklarieren können, wo auch normale, reine Funktionen deklariert werden können, macht es nur Sinn, sie innerhalb von Prozessen zu verwenden. Wenn es in der Architektur deklariert wird, in der wir normalerweise unsere Signale deklarieren, befindet sich keines der Signale zur Kompilierzeit in seinem Gültigkeitsbereich. Daher kann eine unreine Funktion nicht mehr tun als eine reine Funktion, wenn sie in der Architektur oder innerhalb eines Pakets deklariert wird.
Die Motivation für die Verwendung unreiner Funktionen ist hauptsächlich das Entrümpeln des Codes. Wir könnten jedes Signal mit einer reinen Funktion manipulieren, indem wir es einfach zur Parameterliste hinzufügen, aber wenn die Parameterliste zu lang wird, würde es eher verschleiern als vereinfachen.
Die Syntax zum Deklarieren einer unreinen Funktion ist einfach das Schreiben von impure function statt function bei der Deklaration. Informationen zur Syntax einer generischen Funktion finden Sie im Funktions-Tutorial.
Übung
Im vorherigen Tutorial haben wir unseren Finite-State-Machine-Code (FSM) vereinfacht, indem wir eine Funktion zum Berechnen von Zeitverzögerungswerten verwendet haben. Wir haben die Parameter Minuten und Sekunden bereitgestellt, um anzugeben, wie lange wir jede Zustandsänderung verzögern wollten.
Wenn der CounterVal Funktion gab true zurück , die Zeit war abgelaufen und es war Zeit, zum nächsten FSM-Zustand zu gehen. Im selben Vorgang mussten wir auch den Counter zurücksetzen andernfalls würde die Funktion im nächsten Zustand nicht funktionieren. Der Timer wäre bereits abgelaufen.
Die Counter Signal würde immer auf 0 gesetzt werden wenn die Funktion wahr zurückgegeben hat. Wäre es nicht besser, wenn dies im CounterVal passiert wäre Funktion anstelle von mehreren Stellen im Zustandsmaschinencode?
In diesem Video-Tutorial verbessern wir den FSM-Code aus dem vorherigen Tutorial mit einer unreinen Funktion:
Der endgültige Code für die unreine Funktion 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;
Der endgültige Code für das Ampel-Modul :
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;
Die Wellenform, nachdem wir run 5 min eingegeben haben Befehl in der ModelSim-Konsole:
Analyse
Wie wir aus der Wellenform sehen können, bleibt der Modulausgang unverändert, nachdem wir die unreine Funktion hinzugefügt haben. Wir haben die Logik überhaupt nicht geändert, nur den Code.
Die Auswertung der Counter Signal wurde aus dem FSM-Code in die neue unreine Funktion CounterExpired verschoben . Die Counter <= 0; Zeile zum Löschen des Counter Signal wurde auch in die unreine Funktion verschoben.
Das Ergebnis ist ein besser lesbarer FSM-Code, der einfacher gewartet werden kann. Das ist subjektiv, aber für mich CounterExpired(Seconds => 5) ist angenehmer für die Augen als Counter = CounterVal(Seconds => 5) .
Wie weit Sie mit der Verwendung unreiner Funktionen gehen sollten, liegt ganz bei Ihnen und wer auch immer für Ihre Dienste bezahlt. Einige Leute sind der Meinung, dass sie mit Vorsicht verwendet werden sollten, da es schwieriger sein kann, alle Ursachen und Auswirkungen eines in einem Unterprogramm versteckten Algorithmus zu durchschauen. Andere, wie ich, sind der Meinung, dass Code, der leichter zu lesen ist, weniger fehleranfällig ist, solange Sie Ihre Absichten klar machen.
Aus diesem Grund ist es wahrscheinlicher, dass Sie im Testbench-Code unreine Funktionen finden als in Produktionsmodulen. Testbenches sind in der Regel komplexer als das Modul, das sie testen, und die Anforderungen an die Korrektheit des Codes sind weniger streng als für RTL-Code.
Imbiss
- Unreine Funktionen können Signale lesen oder steuern, die nicht auf ihrer Parameterliste stehen
- Es macht nur Sinn unreine Funktionen innerhalb eines Prozesses zu deklarieren
Weiter zum nächsten Tutorial »
VHDL
- Wie verwenden wir Molybdän?
- So erstellen Sie eine Liste von Zeichenfolgen in VHDL
- So stoppen Sie die Simulation in einer VHDL-Testbench
- So erstellen Sie einen PWM-Controller in VHDL
- So generieren Sie Zufallszahlen in VHDL
- So verwenden Sie eine Prozedur in einem Prozess in VHDL
- So verwenden Sie eine Funktion in VHDL
- realloc()-Funktion in der C-Bibliothek:Wie verwenden? Syntax &Beispiel
- free()-Funktion in der C-Bibliothek:Wie verwenden? Lernen Sie mit Beispiel
- So verwenden Sie einen Cutter Grinder