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

Maîtriser les fonctions VHDL : Guide pratique pour un design efficace

Les fonctions sont des sous-programmes en VHDL qui peuvent être utilisés pour implémenter des algorithmes fréquemment utilisés. Une fonction prend zéro ou plusieurs valeurs d'entrée, et elle retourne toujours une valeur. En plus de la valeur de retour, ce qui distingue une fonction d'une procédure, c'est qu'elle ne peut pas contenir d'instructions Wait. Cela signifie que les fonctions consomment toujours zéro temps de simulation.

Si vous êtes familier avec les fonctions ou méthodes d'autres langages de programmation, les fonctions VHDL devraient être faciles à comprendre. En VHDL, nous ne pouvons pas omettre la valeur de retour ou retourner void, une fonction doit toujours retourner quelque chose et la valeur de retour doit être assignée à quelque chose.

Cet article fait partie de la série Tutoriels VHDL de Base.

En VHDL, il y a deux types de fonctions, pures et impures. Qu'une fonction soit pure signifie qu'il ne lui sera pas permis de modifier ou lire un signal externe. Nous pouvons être certains que quand nous appelons une fonction pure avec certains arguments, elle retournera toujours la même valeur. Nous disons que la fonction n'a pas d'effets de bord.

La syntaxe pour déclarer une fonction en VHDL est :

[pure|impure] function <function_name> (
  <parameter1_name> : <parameter1_type> := <default_value>;
  <parameter2_name> : <parameter2_type> := <default_value>;
  ...
) return <return_type> is
  <constant_or_variable_declaration>
begin
  <code_performed_by_the_function>
  return <value>
end function;

Le mot-clé pure/impure est optionnel, bien qu'il défaut à pure si le mot-clé est omis. Tous les paramètres sont traités comme des constantes à l'intérieur de la fonction. Ainsi, ils ne peuvent pas être changés. Les valeurs par défaut sont optionnelles, et la fonction doit toujours se terminer à une instruction return.

Les fonctions ont leur propre région déclarative entre les mots-clés is et begin. Les constantes, signaux ou variables déclarés ici sont valides seulement dans la fonction elle-même, et ils ne conserveront pas leurs valeurs à travers les appels subséquents à la fonction.

Exercice

Dans ce tutoriel, nous allons nous concentrer sur la fonction pure, les fonctions impures seront couvertes dans un tutoriel ultérieur de cette série.

Dans le tutoriel précédent, nous avons créé un module contrôleur de feux tricolores en utilisant une machine d'état finie (FSM). Nous avons copié-collé beaucoup de lignes contenant des calculs de minuteur d'un état à un autre, ne changeant qu'une constante légèrement.

Découvrez comment vous pouvez simplifier le code de la machine d'état en utilisant une fonction :

Le code final pour le testbench de la fonction :

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity T21_FunctionTb is
end entity;

architecture sim of T21_FunctionTb is
  -- Nous utilisons une fréquence d'horloge basse pour accélérer la 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
  -- Le Device Under Test (DUT)
  i_TrafficLights : entity work.T21_TrafficLights(rtl)
    generic map(ClockFrequencyHz => ClockFrequencyHz)
    port map (
      Clk => Clk,
      nRst => nRst,
      NorthRed => NorthRed,
      NorthYellow => NorthYellow,
      NorthGreen => NorthGreen,
      WestRed => WestRed,
      WestYellow => WestYellow,
      WestGreen => WestGreen
    );
    
  -- Processus pour générer l'horloge
  Clk <= not Clk after ClockPeriod / 2;
  
  -- Séquence de testbench
  process is
  begin
    wait until rising_edge(Clk);
    wait until rising_edge(Clk);
    -- Sortir le DUT du reset
    nRst <= '1';
    wait;
  end process;
end architecture;

Le code final pour le module feux tricolores :

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity T21_TrafficLights is
  generic(ClockFrequencyHz : natural);
  port(
    Clk : in std_logic;
    nRst : in std_logic; -- Reset négatif
    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 T21_TrafficLights is
  -- Déclaration de type énuméré et déclaration de signal d'état
  type t_State is (NorthNext, StartNorth, North, StopNorth, WestNext, StartWest, West, StopWest);
  signal State : t_State;
  
  -- Calculer le nombre de cycles d'horloge en minutes/secondes
  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;
  
  -- Compteur pour compter les périodes d'horloge, 1 minute max
  signal Counter : integer range 0 to CounterVal(Minutes => 1) + 1;
  
begin
  process(Clk) is
  begin
    if rising_edge(Clk) then
      if nRst = '0' then
        -- Valeurs de reset
        NorthRed <= '1';
        NorthYellow <= '0';
        NorthGreen <= '0';
        WestRed <= '1';
        WestYellow <= '0';
        WestGreen <= '0';
        State <= NorthNext;
        Counter <= 0;
      else
        -- Valeurs par défaut
        NorthRed <= '0';
        NorthYellow <= '0';
        NorthGreen <= '0';
        WestRed <= '0';
        WestYellow <= '0';
        WestGreen <= '0';
        Counter <= Counter + 1;
        
        case State is
          -- Feu rouge dans toutes les directions
          when NorthNext =>
            NorthRed <= '1';
            WestRed <= '1';
            -- Si 5 secondes se sont écoulées
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State <= StartNorth;
            end if;
          -- Feu jaune dans les directions nord/sud
          when StartNorth =>
            NorthRed <= '1';
            NorthYellow <= '1';
            WestRed <= '1';
            -- Si 5 secondes se sont écoulées
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State <= North;
            end if;
          -- Feu vert dans les directions nord/sud
          when North =>
            NorthGreen <= '1';
            WestRed <= '1';
            -- Si 1 minute s'est écoulée
            if Counter = CounterVal(Minutes => 1) then
              Counter <= 0;
              State <= StopNorth;
            end if;
          -- Feu rouge et jaune dans la direction nord/sud
          when StopNorth =>
            NorthYellow <= '1';
            WestRed <= '1';
            -- Si 5 secondes se sont écoulées
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State <= WestNext;
            end if;
          -- Feu rouge dans toutes les directions
          when WestNext =>
            NorthRed <= '1';
            WestRed <= '1';
            -- Si 5 secondes se sont écoulées
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State <= StartWest;
            end if;
          -- Feu jaune dans la direction ouest/est
          when StartWest =>
            NorthRed <= '1';
            WestRed <= '1';
            WestYellow <= '1';
            -- Si 5 secondes se sont écoulées
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State <= West;
            end if;
          -- Feu vert dans la direction ouest/est
          when West =>
            NorthRed <= '1';
            WestGreen <= '1';
            -- Si 1 minute s'est écoulée
            if Counter = CounterVal(Minutes => 1) then
              Counter <= 0;
              State <= StopWest;
            end if;
          -- Feu rouge et jaune dans la direction ouest/est
          when StopWest =>
            NorthRed <= '1';
            WestYellow <= '1';
            -- Si 5 secondes se sont écoulées
            if Counter = CounterVal(Seconds => 5) then
              Counter <= 0;
              State <= NorthNext;
            end if;
        end case;
      end if;
    end if;
  end process;
end architecture;

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

Maîtriser les fonctions VHDL : Guide pratique pour un design efficace

La forme d'onde avec curseurs ajoutés aux transitions vers et depuis l'état StartNorth :

Maîtriser les fonctions VHDL : Guide pratique pour un design efficace

Analyse

Nous avons remplacé les calculs de minuteur du tutoriel précédent if Counter = ClockFrequencyHz * 5 -1 then par un appel à la nouvelle fonction CounterVal que nous avons créée : if Counter = CounterVal(Seconds => 5) then.

Nous pouvons voir sur la première capture d'écran de forme d'onde que la fonction du module est inchangée. Utiliser des fonctions pour les tâches répétitives est une bonne pratique de conception. Surtout si vous pouvez remplacer les calculs par des lignes plus lisibles contenant des termes comme Minutes et Seconds.

Un autre avantage de l'utilisation des fonctions est que nous pouvons changer l'implémentation de tous les minuteurs à la fois, au lieu de le faire ligne par ligne. Par exemple, si nous avions écrit return TotalSeconds * ClockFrequencyHz; dans la fonction CounterVal, tous les minuteurs auraient duré un cycle d'horloge de trop. Nous pourrions alors changer cela en return TotalSeconds * ClockFrequencyHz -1; dans la fonction CounterVal, et tous les minuteurs seraient corrigés d'un coup.

Si nous examinons la dernière capture d'écran de forme d'onde, nous pouvons voir pourquoi nous devons soustraire 1 de la valeur du minuteur qui est retournée de la fonction CounterVal. Cette forme d'onde examine la durée de l'état StartNorth, il devrait durer exactement cinq secondes. Quand le signal State change vers StartNorth, la valeur Counter est 0, et elle ne change qu'après le prochain cycle d'horloge. Donc, si nous avions compté jusqu'à 500 cycles d'horloge, l'état StartNorth aurait en fait duré 501 cycles. Avec notre testbench tournant à 100 Hz, 500 cycles d'horloge font exactement cinq secondes.

Maîtriser les fonctions VHDL : Guide pratique pour un design efficace

À retenir

Allez au tutoriel suivant »

VHDL

  1. Comment le molybdène façonne l'industrie moderne : usages, avantages et dynamiques d'approvisionnement
  2. Créer des listes de chaînes en VHDL : Bonnes pratiques et exemple
  3. Terminer proprement une simulation de banc de test VHDL
  4. Implémenter un contrôleur PWM en VHDL : Conception, simulation et démo FPGA
  5. Générer des nombres aléatoires en VHDL : De l'uniforme à OSVVM
  6. Exploiter les procédures in-process pour un design FSM VHDL plus propre
  7. Utilisation de realloc() en C : Syntaxe, bonnes pratiques et exemple
  8. Maîtriser la fonction free() en C : Guide pratique et exemple
  9. Maîtriser les techniques de la rectifieuse d'outils : guide pratique