Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Industrial programming >> VHDL

Comment utiliser une fonction impure en VHDL

Une fonction impure peut lire ou écrire n'importe quel signal dans sa portée, y compris ceux qui ne figurent pas dans la liste des paramètres. On dit que la fonction a des effets secondaires .

Ce que nous entendons par effets secondaires, c'est qu'il n'est pas garanti que la fonction renvoie la même valeur à chaque fois qu'elle est appelée avec les mêmes paramètres. Si la fonction peut lire des signaux qui ne figurent pas dans la liste des paramètres, la valeur de retour peut également dépendre de ces paramètres masqués. De plus, la fonction peut modifier des signaux externes qui ne sont pas affectés à partir de sa valeur de retour.

Cet article de blog fait partie de la série de didacticiels VHDL de base.

Bien que nous puissions déclarer des fonctions impures partout où nous pouvons déclarer une fonction normale et pure, cela n'a de sens que de les utiliser dans des processus. Lorsqu'il est déclaré dans l'architecture où nous déclarons normalement nos signaux, aucun des signaux ne sera dans sa portée au moment de la compilation. Ainsi, une fonction impure ne peut rien faire de plus qu'une fonction pure lorsqu'elle est déclarée dans l'architecture ou dans un package.

La motivation pour utiliser des fonctions impures est principalement le désencombrement du code. Nous pourrions manipuler n'importe quel signal avec une fonction pure simplement en l'ajoutant à la liste des paramètres, mais si la liste des paramètres devient trop longue, cela obscurcirait plutôt que simplifierait.

La syntaxe pour déclarer une fonction impure consiste simplement à écrire impure function au lieu de function lors de sa déclaration. Reportez-vous au didacticiel de la fonction pour la syntaxe d'une fonction générique.

Exercice

Dans le didacticiel précédent, nous avons simplifié notre code de machine à états finis (FSM) en utilisant une fonction de calcul des valeurs de retard. Nous avons fourni les paramètres Minutes et Secondes pour spécifier combien de temps nous voulions retarder chaque changement d'état.

Si le CounterVal la fonction a renvoyé true , le temps était écoulé et il était temps de passer à l'état FSM suivant. Dans le même processus, nous avons également dû réinitialiser le Counter signal, sinon la fonction ne fonctionnerait pas dans l'état suivant. Le minuteur aurait déjà expiré.

Le Counter le signal serait toujours défini sur 0 lorsque la fonction retourne true. Ne serait-il pas préférable que cela se produise dans le CounterVal fonction au lieu de plusieurs endroits dans le code de la machine d'état ?

Dans ce tutoriel vidéo, nous allons améliorer le code FSM du tutoriel précédent en utilisant une fonction impure :

Le code final pour la fonction impure 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;

Le code final pour le module des feux de circulation :

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;

La forme d'onde après avoir entré le run 5 min commande dans la console ModelSim :

Analyse

Comme nous pouvons le voir sur la forme d'onde, la sortie du module reste inchangée après l'ajout de la fonction impure. Nous n'avons pas du tout changé la logique, seulement le code.

L'évaluation du Counter le signal a été déplacé du code FSM vers la nouvelle fonction impure CounterExpired . Le Counter <= 0; ligne pour effacer le Counter le signal a également été déplacé dans la fonction impure.

Le résultat est un code FSM plus lisible qui peut être maintenu plus facilement. C'est subjectif, mais pour moi CounterExpired(Seconds => 5) est plus agréable pour les yeux que Counter = CounterVal(Seconds => 5) .

Jusqu'où vous devez aller avec l'utilisation de fonctions impures dépend entièrement de vous et de celui qui paie pour vos services. Certaines personnes pensent qu'ils doivent être utilisés avec prudence car il peut être plus difficile de voir à travers toutes les causes et les effets d'un algorithme caché dans un sous-programme. D'autres, comme moi, pensent que tant que vous clarifiez vos intentions, plus le code est facile à lire, moins il est sujet aux erreurs.

Pour cette raison, vous êtes plus susceptible de trouver des fonctions impures dans le code de testbench que dans les modules de production. Les bancs de test sont généralement plus complexes que le module qu'ils testent, et l'exigence d'exactitude du code est moins stricte que pour le code RTL.

À emporter

Aller au tutoriel suivant »


VHDL

  1. Comment utilisons-nous le molybdène?
  2. Comment créer une liste de chaînes en VHDL
  3. Comment arrêter la simulation dans un testbench VHDL
  4. Comment créer un contrôleur PWM en VHDL
  5. Comment générer des nombres aléatoires en VHDL
  6. Comment utiliser une procédure dans un processus en VHDL
  7. Fonction realloc() dans la bibliothèque C :comment l'utiliser ? Syntaxe et exemple
  8. Fonction free() dans la bibliothèque C :comment l'utiliser ? Apprendre avec l'exemple
  9. Comment utiliser une meuleuse de coupe