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