Comment utiliser une fonction en VHDL
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 renvoie 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 connaissez les fonctions ou les 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 renvoyer void, une fonction doit toujours retourner quelque chose et la valeur de retour doit être affectée à quelque chose.
Cet article de blog fait partie de la série de didacticiels VHDL de base.
En VHDL, il existe deux types de fonctions, pures et impur les fonctions. Qu'une fonction soit pure signifie qu'elle ne sera pas autorisée à modifier ou à lire un signal externe. Nous pouvons être certains que lorsque nous appelons une fonction pure avec certains arguments, elle renverra toujours la même valeur. Nous disons que la fonction n'a pas d'effets secondaires .
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é pur/impur est facultatif, bien qu'il soit par défaut pur 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 modifiés. Les valeurs par défaut sont facultatives et la fonction doit toujours se terminer à un return
déclaration.
Les fonctions ont leur propre région déclarative entre le in
et begin
mots clés. Les constantes, signaux ou variables déclarés ici ne sont valides qu'au sein de la fonction elle-même, et ils ne conserveront pas leurs valeurs lors d'appels ultérieurs à la fonction.
Exercice
Dans ce didacticiel, nous allons nous concentrer sur la fonction pure, les fonctions impures seront couvertes dans un didacticiel ultérieur de cette série.
Dans le didacticiel précédent, nous avons créé un module de contrôleur de feux de circulation à l'aide d'une machine à états finis (FSM). Nous avons copié-collé de nombreuses lignes contenant des calculs de minuterie d'un état à un autre, en ne modifiant que légèrement une constante.
Découvrez comment simplifier le code de la machine d'état à l'aide d'une fonction :
Le code final pour la fonction testbench :
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 -- 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.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); -- 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 T21_TrafficLights is generic(ClockFrequencyHz : natural); 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 T21_TrafficLights is -- Enumerated type declaration and state signal declaration type t_State is (NorthNext, StartNorth, North, StopNorth, WestNext, StartWest, West, StopWest); signal State : t_State; -- 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; -- Counter for counting clock periods, 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 -- Reset values NorthRed <= '1'; NorthYellow <= '0'; NorthGreen <= '0'; WestRed <= '1'; WestYellow <= '0'; WestGreen <= '0'; State <= NorthNext; Counter <= 0; else -- Default values NorthRed <= '0'; NorthYellow <= '0'; NorthGreen <= '0'; WestRed <= '0'; WestYellow <= '0'; WestGreen <= '0'; Counter <= Counter + 1; case State is -- Red light in all directions when NorthNext => NorthRed <= '1'; WestRed <= '1'; -- If 5 seconds have passed if Counter = CounterVal(Seconds => 5) then Counter <= 0; State <= StartNorth; end if; -- Yellow light in north/south directions when StartNorth => NorthRed <= '1'; NorthYellow <= '1'; WestRed <= '1'; -- If 5 seconds have passed if Counter = CounterVal(Seconds => 5) then Counter <= 0; State <= North; end if; -- Green light in north/south directions when North => NorthGreen <= '1'; WestRed <= '1'; -- If 1 minute has passed if Counter = CounterVal(Minutes => 1) then Counter <= 0; State <= StopNorth; end if; -- Red and yellow light in north/south direction when StopNorth => NorthYellow <= '1'; WestRed <= '1'; -- If 5 seconds have passed if Counter = CounterVal(Seconds => 5) then Counter <= 0; State <= WestNext; end if; -- Red light in all directions when WestNext => NorthRed <= '1'; WestRed <= '1'; -- If 5 seconds have passedf if Counter = CounterVal(Seconds => 5) then Counter <= 0; State <= StartWest; end if; -- Yellow light in west/east direction when StartWest => NorthRed <= '1'; WestRed <= '1'; WestYellow <= '1'; -- If 5 seconds have passed if Counter = CounterVal(Seconds => 5) then Counter <= 0; State <= West; end if; -- Green light in west/east direction when West => NorthRed <= '1'; WestGreen <= '1'; -- If 1 minute has passed if Counter = CounterVal(Minutes => 1) then Counter <= 0; State <= StopWest; end if; -- Red and yellow light in west/east direction when StopWest => NorthRed <= '1'; WestYellow <= '1'; -- If 5 seconds have passed 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 saisi le run 5 min
commande dans la console ModelSim :
La forme d'onde avec des curseurs ajoutés aux transitions vers et depuis le StartNorth
état :
Analyse
Nous avons remplacé les calculs de minuterie du tutoriel précédent if Counter = ClockFrequencyHz * 5 -1 then
avec un appel au nouveau CounterVal
fonction que nous avons créée :if Counter = CounterVal(Seconds => 5) then
.
Nous pouvons voir sur la première capture d'écran de la forme d'onde que la fonction du module est inchangée. L'utilisation de fonctions pour des 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 modifier l'implémentation de tous les temporisateurs à la fois, au lieu de le faire ligne par ligne. Par exemple, si nous avions écrit return TotalSeconds * ClockFrequencyHz;
dans le CounterVal
fonction, tous les temporisateurs auraient duré un cycle d'horloge de trop. Nous pourrions alors changer cela en return TotalSeconds * ClockFrequencyHz -1;
dans le CounterVal
fonction, et toutes les minuteries seraient fixées à la fois.
Si nous examinons la dernière capture d'écran de la forme d'onde, nous pouvons voir pourquoi nous devons soustraire 1 de la valeur de la minuterie renvoyée par le CounterVal
fonction. Cette forme d'onde examine la durée du StartNorth
état, il devrait durer exactement cinq secondes. Lorsque le State
le signal passe à StartNorth
, le Counter
la valeur est 0 et ne change qu'après le cycle d'horloge suivant. Donc, si nous avions compté jusqu'à 500 cycles d'horloge, le StartNorth
l'état aurait en fait duré 501 cycles. Avec notre banc d'essai fonctionnant à 100 Hz, 500 cycles d'horloge correspondent exactement à cinq secondes.
À emporter
- Les fonctions peuvent accepter zéro ou plusieurs paramètres, mais elles renvoient toujours une valeur
- Les fonctions ne peuvent pas contenir
wait
déclarations - Les fonctions pures ne peuvent pas avoir d'effets secondaires, contrairement aux fonctions impures.
Aller au tutoriel suivant »
VHDL
- Comment utilisons-nous le molybdène?
- Comment créer une liste de chaînes en VHDL
- Comment arrêter la simulation dans un testbench VHDL
- Comment créer un contrôleur PWM en VHDL
- Comment générer des nombres aléatoires en VHDL
- Comment utiliser une procédure dans un processus en VHDL
- Fonction realloc() dans la bibliothèque C :comment l'utiliser ? Syntaxe et exemple
- Fonction free() dans la bibliothèque C :comment l'utiliser ? Apprendre avec l'exemple
- Comment utiliser une meuleuse de coupe