Fonctions Verilog expliquées : syntaxe, exemples et différence avec les tâches

目次

1. Qu’est‑ce qu’une fonction Verilog ? (Concept de base et rôle)

Verilog HDL (Hardware Description Language) est un langage de description matériel utilisé pour concevoir et simuler des circuits numériques. Parmi ses fonctionnalités, la fonction est un mécanisme qui vous permet de modulariser des opérations spécifiques et de les rendre réutilisables. Comprendre les fonctions Ver non seulement la lisibilité et la maintenabilité du code, mais conduit également à une conception de circuits plus efficace. Dans cet article, nous expliquons le concept de base des fonctions Verilog et leur utilisation dans la conception réelle.

Qu’est‑ce qu’une fonction ?

Une fonction Verilog est un bloc qui effectue un calcul ou une opération spécifique et renvoie une seule valeur. En utilisant des fonctions, vous pouvez réduire le code redondant et simplifier la description de votre circuit.

Caractéristiques principales des fonctions

  • Un ou plusieurs entrées peuvent être spécifiées (seul input est autorisé)
  • Une seule sortie (la valeur de retour de la fonction)
  • Les délais temporels (ex. #10) ne sont pas autorisés
  • Les fonctions doivent toujours décrire une logique combinatoire
  • Les fonctions sont définies en dehors des blocs always et sont évaluées immédiatement (contrairement aux tasks)

Quand utiliser les fonctions Verilog

Les fonctions Verilog sont principalement utilisées dans les scénarios suivants :

1. Décrire une logique combinatoiretant donné que les fonctions renvoient les résultats immédiatement en fonction des entrées, elles sont souvent utilisées dans la logique combinatoire.

Exemples : addition, soustraction, encodeurs, décodeurs et autres opérations arithmétiques.

2. Améliorer la réutilisabilité du code

Vous pouvez éliminer le code redondant en résumant la logique fréquemment utilisée dans des fonctions. Exemple : transformer une expression conditionnelle complexe en fonction pour améliorer la lisibilité au sein d’un module.

3. Réduire les erreurs de conception

En centralisant les calculs ou les opérations logiques dans une fonction unique, vous pouvez réduire les erreurs lors de la modification du code. Exemples : calculs CRC (Cyclic Redundancy Check) ou vérifications de parité.

Différence entre fonctions et tâches

En Verilog, il existe un autre construit appelé task. Bien que fonctions et tâches soient similaires, elles diffèrent de manière importante :
ItemFonctionTâche
SortieUn seulAutoriser plusieurs
Veuillez fournir le fragment HTML à traduire en français.OuiOui
Local VariablesAutoriséAutorisé
Délai (#10)Non autoriséAutorisé
Utilisation à l’intérieur de alwaysAutoriséNon autorisé
Invocationfunction_name(arguments)task_name(arguments);

Quand utiliser une fonction

  • Lorsque vous avez besoin d’un résultat de calcul immédiat
  • Pour une logique qui n’inclut pas de délais
  • Lorsque une seule valeur de retour suffit

Quand utiliser une tâche

  • Lorsque le processus inclut des délais (ex. #10)
  • Lorsque plusieurs sorties sont requ
  • À des fins de simulation/débogage (ex. sortie de journal)

Résumé

  • Une fonction Verilog est une fonction qui prend des entrées et renvoie une seule valeur.
  • Elle est idéale pour décrire une logique combinatoire et ne peut pas inclure de délais.
  • Utile pour réduire le code redondant et améliorer la lisibilité.
  • Les fonctions et les tâches diffèrent ; choisir la bonne dépend de votre cas d’utilisation.

2. Comment écrire une fonction Verilog [Beginner-Friendly Example]

Dans la section précédente, nous avons couvert le concept de base des fonctions Verilog. Ici, nous allons plonger dans la syntaxe réelle et comment écrire des fonctions Verilog en pratique.

Syntaxe de base d’une fonction

Une fonction Verilog s’écrit en utilisant la syntaxe générale suivante :
function [output_bit_width] function_name;
    input [input_bit_width] input1, input2, ...;
    begin
        function_name = expression;
    end
endfunction

Points clés

  • Déclarer avec le mot‑clé function
  • La valeur de retour est assignée à une variable portant le même nom que la fonction
  • Déclarer les entrées avec input (vous ne pouvez pas utiliser output ou inout )
  • Effectuer les calculs à l’intérieur de begin ... end
  • Définir la fonction en dehors d’un bloc always

Exemple simple d’une fonction Verilog

L’exemple suivant montre une fonction qui effectue une addition sur 8 bits :
module example;
    function [7:0] add_function;
        input [7:0] a, b;
        begin
            add_function = a + b;
        end
    endfunction

    reg [7:0] x, y, sum;

    initial begin
        x = 8'b00001100; // 12
        y = 8'b00000101; // 5
        sum = add_function(x, y);
        $display("Sum: %d", sum); // Sum: 17
    end
endmodule

Explication

  • add_function prend deux entrées de 8 bits (a et b) et renvoie leur somme
  • La fonction est appelée avec sum = add_function(x, y); et assigne le résultat à sum
  • Le bloc initial utilise $display pour afficher le résultat

Déclaration des entrées et sorties dans une fonction

Déclaration des entrées

Une fonction Verilog ne peut prendre que des arguments input.
function [7:0] my_function;
    input [7:0] in1, in2;
    begin
        my_function = in1 & in2; // AND operation
    end
endfunction
Remarque : Vous ne pouvez pas déclarer output dans une fonction. La valeur de retour est toujours la variable portant le même nom que la fonction.

Fonction avec instructions conditionnelles

Vous pouvez également utiliser des instructions if ou case à l’intérieur d’une fonction.
function [3:0] max_function;
    input [3:0] a, b;
    begin
        if (a > b)
            max_function = a;
        else
            max_function = b;
    end
endfunction
Cette fonction renvoie la plus grande valeur entre a et b.

Résumé

  • Une fonction Verilog est définie avec le mot‑clé function et renvoie une seule valeur
  • Seuls les arguments input sont autorisés (pas de output)
  • La valeur de retour est assignée à la variable portant le même nom que la fonction
  • Les instructions if et case peuvent être utilisées pour la logique conditionnelle

3. Comment utiliser les fonctions Verilog [With Practical Code Examples]

Dans la section précédente, nous avons appris la syntaxe de base et comment écrire des fonctions Verilog. Ici, nous expliquerons comment appliquer les fonctions dans une conception réelle avec des exemples pratiques.

Comment appeler une fonction

Une fonction Verilog est appelée comme une affectation de variable ordinaire, en utilisant le format function_name(arg1, arg2, ...). L’exemple suivant définit une fonction XOR de 8 bits et l’utilise à l’intérieur d’un module :
module function_example;
    function [7:0] xor_function;
        input [7:0] a, b;
        begin
            xor_function = a ^ b;
        end
    endfunction

    reg [7:0] x, y, result;

    initial begin
        x = 8'b11001100;
        y = 8'b10101010;
        result = xor_function(x, y); // calling the function
        $display("XOR Result: %b", result); // XOR Result: 01100110
    end
endmodule

Points clés

  • Une fonction est appelée sous la forme variable = function(arguments);
  • Elle peut être utilisée à l’intérieur de blocs always ou initial
  • Les fonctions fonctionnent comme de la logique combinatoire

Utilisation des fonctions dans la logique combinatoire

Comme les fonctions Verilog sont toujours évaluées immédiatement, elles sont utiles pour construire de la logique combinatoire. L’exemple suivant montre un décodeur 2‑to‑4 implémenté avec une fonction :
module decoder_example;
    function [3:0] decoder;
        input [1:0] sel;
        begin
            case (sel)
                2'b00: decoder = 4'b0001;
                2'b01: decoder = 4'b0010;
                2'b10: decoder = 4'b0100;
                2'b11: decoder = 4'b1000;
                default: decoder = 4'b0000;
            endcase
        end
    endfunction

    reg [1:0] select;
    wire [3:0] decoded_output;

    assign decoded_output = decoder(select); // using the function

    initial begin
        select = 2'b01;
        #10; // add delay to observe simulation changes
        $display("Decoded Output: %b", decoded_output); // Decoded Output: 0010
    end
endmodule

Explication

  • La fonction decoder convertit une entrée de 2 bits en une sortie de décodeur de 4 bits
  • Utilise une instruction case pour déterminer la sortie en fonction de l’entrée
  • assign est utilisé pour mapper la sortie de la fonction à decoded_output → La fonction fonctionne comme une partie de la logique combinatoire

Fonctions vs. Blocs always [Comparison Table]

Les fonctions Verilog et les blocs always sont tous deux utilisés pour décrire la logique, mais leur but et leurs restrictions diffèrent.
ItemFonctionToujours bloquer
Emplacement de la définitionEn dehors des blocs alwaysDans les blocs always
Entréesinputregwire
SortiesUne seule valeurPeut mettre à jour plusieurs valeurs
Délai (#10)Non autoriséAutorisé
Rétention d’étatNon autoriséAutorisé
Main UsageLogique combinatoireLogique séquentielle ou traitement basé sur les événements

Directives clés

  • Utilisez les fonctions pour simplifier les opérations logiques simples (logique combinatoire)
  • Utilisez les blocs always pour les circuits qui conservent un état (par ex., bascules)
  • Si vous avez besoin de délais (comme #10), utilisez des blocs always plutôt que des fonctions

Résumé : Comment utiliser les fonctions Verilog

✅ Appelez une fonction avec function_name(arguments)Les fonctions sont idéales pour la logique combinatoire et diffèrent des blocs always ✅ Utilisez les instructions case ou if pour décrire une logique flexible ✅ Utile pour les décodeurs, les opérations arithmétiques, et plus

4. Applications pratiques des fonctions Verilog (Conception de décodeur et d’ALU)

Jusqu’à présent, nous avons appris la syntaxe de base et l’utilisation des fonctions Verilog. Dans cette section, nous examinerons comment appliquer les fonctions dans la conception de circuits numériques réels, en utilisant des décodeurs et des ALU (Unités Arithmétiques et Logiques) comme exemples.

Implémentation fonctionnelle d’un décodeur (décodeur 2‑à‑4)

Un décodeur est un circuit qui convertit un petit nombre de bits d’entrée en un plus grand nombre de bits de sortie. Par exemple, un décodeur 2‑à‑4 convertit une entrée de 2 bits en une sortie de 4 bits. Implémentons cela à l’aide d’une fonction :
module decoder_example;
    function [3:0] decoder;
        input [1:0] sel;
        begin
            case (sel)
                2'b00: decoder = 4'b0001;
                2'b01: decoder = 4'b0010;
                2'b10: decoder = 4'b0100;
                2'b11: decoder = 4'b1000;
                default: decoder = 4'b0000;
            endcase
        end
    endfunction

    reg [1:0] select;
    wire [3:0] decoded_output;

    assign decoded_output = decoder(select); // using the function

    initial begin
        select = 2'b00; #10;
        $display("Decoded Output: %b", decoded_output);
        select = 2'b01; #10;
        $display("Decoded Output: %b", decoded_output);
        select = 2'b10; #10;
        $display("Decoded Output: %b", decoded_output);
        select = 2'b11; #10;
        $display("Decoded Output: %b", decoded_output);
    end
endmodule

Implémentation fonctionnelle d’une ALU (Addition, Soustraction, AND, OR)

Une ALU (Unité Arithmétique et Logique) est le circuit central d’un CPU, responsable d’exécuter des opérations arithmétiques et logiques telles que l’addition, la soustraction, AND et OR. Ici, nous concevons une simple ALU 8 bits en utilisant une fonction Verilog :
module alu_example;
    function [7:0] alu;
        input [7:0] a, b;
        input [1:0] op; // 2-bit control signal
        begin
            case (op)
                2'b00: alu = a + b; // Addition
                2'b01: alu = a - b; // Subtraction
                2'b10: alu = a & b; // AND
                2'b11: alu = a | b; // OR
                default: alu = 8'b00000000;
            endcase
        end
    endfunction

    reg [7:0] x, y;
    reg [1:0] opcode;
    wire [7:0] result;

    assign result = alu(x, y, opcode); // using the function

    initial begin
        x = 8'b00001100; // 12
        y = 8'b00000101; // 5

        opcode = 2'b00; #10;
        $display("Addition Result: %d", result); // 12 + 5 = 17

        opcode = 2'b01; #10;
        $display("Subtraction Result: %d", result); // 12 - 5 = 7

        opcode = 2'b10; #10;
        $display("AND Result: %b", result); // AND operation

        opcode = 2'b11; #10;
        $display("OR Result: %b", result); // OR operation
    end
endmodule

Résumé

Les fonctions peuvent être utilisées efficacement dans les circuits combinatoires tels que les décodeurs et les ALUL’utilisation d’instructions case permet des descriptions d’opérations flexiblesLes fonctions améliorent la lisibilité et rendent la conception plus réutilisableLes fonctions conviennent le mieux à la logique combinatoire, mais pas aux circuits séquentiels (car elles ne peuvent pas inclure de délais)

5. Considérations importantes lors de l’utilisation des fonctions Verilog

Les fonctions Verilog sont des outils puissants qui améliorent la lisibilité et la réutilisabilité du code, mais elles comportent également certaines restrictions. Dans cette section, nous expliquerons les points clés dont vous devez être conscient lors de l’utilisation des fonctions.

Les appels récursifs ne sont pas autorisés

Dans les fonctions Verilog, les appels récursifs sont interdits. Cela signifie qu’une fonction ne peut pas s’appeler elle‑même à l’intérieur de son propre corps.

❌ Exemple NG : Fonction récursive

function [3:0] factorial;
    input [3:0] n;
    begin
        if (n == 0)
            factorial = 1;
        else
            factorial = n * factorial(n - 1); // ❌ Recursive call not allowed
    end
endfunction
Ce code entraînera une erreur de simulation.

✅ Solution : Utiliser des boucles à la place

Si la récursion est nécessaire, utilisez une boucle à l’intérieur d’un bloc always ou d’une tâche à la place.
task factorial_task;
    input [3:0] n;
    output [15:0] result;
    integer i;
    begin
        result = 1;
        for (i = 1; i <= n; i = i + 1)
            result = result * i;
    end
endtask
En utilisant des boucles, la récursion peut être évitée.

Les délais temporels (#10) ne peuvent pas être utilisés dans les fonctions

Puisque les fonctions Verilog sont évaluées immédiatement (fonctionnent comme une logique combinatoire), elles ne peuvent pas inclure de délais temporels comme #10.

❌ Exemple NG : Délai à l’intérieur d’une fonction

function [7:0] delay_function;
    input [7:0] in;
    begin
        #10; // ❌ Delay not allowed inside functions
        delay_function = in + 1;
    end
endfunction
Ce code provoquera une erreur de compilation.

✅ Solution : Utiliser des blocs always à la place

Si des délais sont requis, utilisez un bloc always ou une tâche à la place d’une fonction.
task delay_task;
    input [7:0] in;
    output [7:0] out;
    begin
        #10;
        out = in + 1;
    end
endtask
En bref, utilisez des tâches les opérations qui impliquent des délais.

Choisir entre fonctions et tâches

En Verilog, il existe à la fois des fonctions et des tâches. Bien qu’elles se ressemblent, elles ont des cas d’utilisation différents et doivent être choisies de manière appropriée.
ItemFonctionTâche
SortieUn seulMultiple autorisé
Entréeinputinputoutput
Local VariablesAutoriséAutorisé
Délai (#10)Non autoriséAutorisé
Utilisation à l’intérieur de alwaysAutoriséNon autorisé
Invocationfunction_name(arguments)task_name(arguments);

Quand utiliser une fonction

✅ Lorsque vous avez besoin d’un résultat de calcul immédiat (par ex., addition, soustraction, opérations logiques) ✅ Pour décrire une logique combinatoire sans délais ✅ Lorsque qu’une seule valeur de retour est requise

Quand utiliser une tâche

✅ Lorsque des délais (#10, etc.) sont requis ✅ Lorsque plusieurs sorties sont nécessaires ✅ Pour les opérations de simulation/débogage (surveillance, affichage, etc.)

Les fonctions ne peuvent pas être définies à l’intérieur de blocs always

Les fonctions Verilog ne peuvent pas être définies à l’intérieur d’un bloc always. Les fonctions doivent être définies à l’extérieur puis appelées à l’intérieur d’un always.

❌ Exemple NG : Définir une fonction à l’intérieur d’un always

always @(a or b) begin
    function [7:0] my_function; // ❌ Not allowed inside always
        input [7:0] x, y;
        begin
            my_function = x + y;
        end
    endfunction
end
Ce code entraînera une erreur de compilation.

✅ Exemple correct

Définissez la fonction en dehors du always et appelez‑la à l’intérieur :
function [7:0] add_function;
    input [7:0] x, y;
    begin
        add_function = x + y;
    end
endfunction

always @(a or b) begin
    result = add_function(a, b); // ✅ call the function
end

Résumé

Les fonctions Verilog ont plusieurs restrictionsPas d’appels récursifs (utilisez des boucles ou des tâches à la place) ✅ Pas de délais (#10) (utilisez always ou des tâches à la place) ✅ Ne peuvent pas être définies à l’intérieur de blocs always (doivent être définies à l’extérieur) ✅ Une seule valeur de retour (utilisez des tâches si plusieurs sorties sont nécessaires) ✅ Utilisez les fonctions et les tâches de manière appropriée selon la situation

6. [FAQ] Questions fréquemment posées sur les fonctions Verilog

Jusqu’à présent, nous avons couvert les bases, l’utilisation avancée et les considérations importantes des fonctions Verilog. Dans cette section, nous résumons les questions fréquemment posées et leurs réponses sur les fonctions Verilog.

Quelle est la différence entre une fonction et une tâche ?

Q. Quelle est la différence entre une fonction Verilog et une tâche ? Laquelle devrais-je utiliser ?

A. Une fonction est utilisée lorsque vous avez besoin de « renvoyer une seule valeur immédiatement », tandis qu’une tâche est utilisée lorsque vous avez besoin de « plusieurs sorties ou d’opérations impliquant des délais ».

ItemFonction
Could you please provide the HTML snippet you’d like translated?Un seulAutoriser plusieurs
Could you please provide the HTML snippet you’d like translated into French?inputinputoutput
Local VariablesAutoriséAutorisé
Délai (#10)Non autoriséAutorisé
Utilisation à l’intérieur alwaysAutoriséNon autorisé
Invocationfunction_name(arguments)task_name(arguments);

Quand utiliser une fonction

✅ Lorsque vous avez besoin d’un résultat de calcul immédiat (par ex., addition, soustraction, opérations logiques) ✅ Pour décrire une logique combinatoire sans délais ✅ Lorsqu’une seule valeur de retour est nécessaire

Quand utiliser une tâche

✅ Lorsque vous avez besoin de délais (par ex., #10) ✅ Lorsque plusieurs sorties sont requises ✅ Lors de l’écriture de code de débogage/surveillance pour la simulation

Q. Puis-je utiliser reg à l’intérieur d’une fonction ?

A. Non, vous ne pouvez pas utiliser reg à l’une fonction, mais vous pouvez utiliser integer à la place.

Dans les fonctions Verilog, vous ne pouvez pas déclarer de variables de type reg, mais vous pouvez utiliser integer pour les calculs.

✅ Exemple correct (utilisant integer)

function [7:0] multiply;
    input [3:0] a, b;
    integer temp;
    begin
        temp = a * b;
        multiply = temp;
    end
endfunction

Quand devrais-je utiliser des fonctions ?

Q. Dans quelles situations-il approprié d’utiliser des fonctions ?

A. Les fonctions conviennent le mieux aux « opérations arithmétiques simples » et à la « logique combinatoire ».

Exemples où les fonctions sont utiles incluent :
  • Opérations arithmétiques (addition, soustraction, logique)
  • Décodeurs et encodeurs
  • Comparaisons (trouver les valeurs maximale/minimale)
  • Vérification d’erreurs (par ex., contrôle de parité)
Cependant, les fonctions ne conviennent pas aux circuits séquentiels qui incluent des bascules.

Q. Une fonction Verilog peut-elle appeler une autre fonction à l’intérieur ?

A. Oui, une fonction peut appeler une autre fonction, mais soyez prudent avec les dépendances.

function [7:0] add;
    input [7:0] a, b;
    begin
        add = a + b;
    end
endfunction

function [7:0] double_add;
    input [7:0] x, y;
    begin
        double_add = add(x, y) * 2; // calling another function
    end
endfunction

Q. Comment devrais-je décider d’utiliser une fonction ou un bloc always ?

A. Les fonctions sont utilisées pour la « logique combinatoire », tandis que les blocs always sont utilisés pour la « logique séquentielle ».

ItemFonctionToujours bloquer
Délai (#10)Non autoriséAutorisé
Conservation d’étatNon autoriséAutorisé
Utilisation principaleLogique combinatoire (calculs instantanés)Logique séquentielle (bascule, compteurs)
Par exemple, lors d’une addition :

✅ Fonction (Logique combinatoire)

function [7:0] add;
    input [7:0] a, b;
    begin
        add = a + b;
    end
endfunction

✅ Bloc always (Logique séquentielle)

always @(posedge clk) begin
    sum <= a + b; // works as a flip-flop
end

Résumé

Les fonctions sont idéales pour les opérations simples et la logique combinatoireComprenez les différences entre fonctions et tâches et utilisez-les de manière appropriéeLes blocs always sont pour la logique séquentielle, les fonctions sont pour la logique combinatoireLes fonctions ne peuvent pas inclure de délais (#10) ou de tableaux — utilisez des tâches ou des modules à la place