Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial programming >> VHDL

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

Weiter zum nächsten Tutorial »


VHDL

  1. Wie verwenden wir Molybdän?
  2. So erstellen Sie eine Liste von Zeichenfolgen in VHDL
  3. So stoppen Sie die Simulation in einer VHDL-Testbench
  4. So erstellen Sie einen PWM-Controller in VHDL
  5. So generieren Sie Zufallszahlen in VHDL
  6. So verwenden Sie eine Prozedur in einem Prozess in VHDL
  7. So verwenden Sie eine Funktion in VHDL
  8. realloc()-Funktion in der C-Bibliothek:Wie verwenden? Syntax &Beispiel
  9. free()-Funktion in der C-Bibliothek:Wie verwenden? Lernen Sie mit Beispiel
  10. So verwenden Sie einen Cutter Grinder