Connaissance de base du FPGA----Chapitre 3 Section 5 Description fonctionnelle - Logique de combinaison

Section 5 Description fonctionnelle - Logique de combinaison

5.1 Énoncés de programme

5.1.1 instruction d'affectation

L'instruction assign est une instruction d'affectation continue. Généralement, la valeur d'une variable est affectée à une autre variable sans interruption . Les deux variables sont similaires à être reliées par un fil, qui est utilisé comme une connexion. Le format de base d'une instruction d'affectation est :

affecter a = b (opérateur logique) c ... ;

La fonction de l'instruction assign appartient à la catégorie de la logique combinatoire et son champ d'application peut être résumé comme suit :

(1) affectation continue ;

(2) Connexion ;

(3) Attribuez une valeur à la variable de type de fil . Wire est un réseau de fils, qui équivaut à la ligne de connexion réelle. Si vous souhaitez utiliser assign pour vous connecter directement, utilisez la variable de type de fil. La valeur de la variable de type de fil change à tout moment.

Il convient de noter que plusieurs instructions d'affectation continue assign sont exécutées indépendamment et en parallèle.

5.1.2 L'instruction toujours

L'instruction always est une instruction de boucle conditionnelle, et le mécanisme d'exécution est réalisé en pilotant un événement appelé table de variables sensibles , qui sera décrit en détail ci-dessous. Le format de base de l'instruction always est :

toujours @(événement sensible)begin

déclaration de programme

fin

toujours signifie "toujours, toujours", @ est suivi d'événements. Le tout signifie toujours : lorsque la condition de l'événement sensible est remplie, exécutez l'"instruction de programme" une fois. Chaque fois qu'un événement sensible est satisfait, "l'instruction de programme" est exécutée une fois. (Lorsque la condition sensible dans l'événement sensible change, exécutez le contenu dans l'instruction de boucle toujours conditionnelle

image-20211029113455362

La signification de ce programme est : Lorsque le signal a, le signal b ou le signal d change, exécutez l'instruction suivante une fois. Lors de l'exécution de cette instruction, jugez d'abord si le signal sel est 0, et s'il est 0, exécutez la troisième ligne de code. Si sel n'est pas 0, exécutez la cinquième ligne de code. Il convient de souligner que si l'un quelconque de a, b et c change une fois, les lignes 2 à 5 ne seront exécutées qu'une seule fois et ne seront pas exécutées une deuxième fois.

Il convient de noter ici que seul le changement du signal sel n'exécutera pas les codes de la ligne 2 à la ligne 5, ce qui n'est généralement pas conforme à l'idée du concepteur. Par exemple, l'idée générale du concepteur est la suivante : lorsque sel vaut 0, le résultat de c est a+b ; lorsque sel n'est pas 0, le résultat de c est a+d. Mais si la condition de déclenchement ne change pas, bien que sel passe de 0 à 1, le résultat de c est toujours a+b. Il ne s'agit donc pas d'un design thinking canonique.

Par conséquent, reconcevez le code selon l'idée du concepteur : lorsque le signal a ou le signal b ou le signal d ou le signal sel change, exécutez les lignes 2 à 5. De cette manière, on peut s'assurer que lorsque la valeur de signal de sel est 0, le résultat de c doit être a+b, et lorsque sel n'est pas 0, le résultat de c doit être a+d. Par conséquent, pour ajouter sel à la liste sensible, le code est le suivant.

image-20211029113540684

Lorsqu'il y a de nombreux signaux sensibles, il est facile de manquer les signaux sensibles. Pour éviter cette situation, vous pouvez utiliser "*" à la place. Ce "*" fait référence à tous les signaux conditionnels dans "l'instruction du programme", c'est-à-dire a, b, d, sel (sans c), et cette méthode d'écriture est également recommandée, et le code spécifique est le suivant.

image-20211029113600878

Ce type d'instruction toujours dans laquelle le résultat d'un changement de signal conditionnel change immédiatement est appelé "logique combinatoire".

image-20211029113618567

La liste de sensibilité de code ci-dessus est ** "posedge clk", où posedge signifie front montant **. C'est-à-dire que lorsque clk passe de 0 à 1, le code du programme est exécuté une fois, c'est-à-dire les lignes 2 à 5, et la valeur de c reste inchangée à d'autres moments. Il convient de souligner que si clk ne passe pas de 0 à 1, alors même si a, b, d, sel changent, la valeur de c reste inchangée.

image-20211029113640663

On peut voir que la liste sensible du code ci-dessus est "negedge clk", où negedg représente le front descendant . C'est-à-dire que lorsque clk passe de 1 à 0, le code du programme est exécuté une fois, c'est-à-dire les lignes 2 à 5, et la valeur de c reste inchangée à d'autres moments. Il convient de souligner que si clk ne passe pas de 1 à 0, même si a, b, d, sel changent, la valeur de c reste inchangée.

image-20211029113709007

La liste sensible du code ci-dessus est "posedge clk or negedge rst_n", c'est-à-dire que lorsque clk passe de 0 à 1, ou lorsque rst_n passe de 1 à 0, le code du programme est exécuté une fois, c'est-à-dire lignes 2 à 8, autres La valeur de c reste inchangée à l'instant. Ce type de déclenchement de front de signal, c'est-à-dire que le signal ne change toujours que sur le front montant ou descendant, est appelé "logique séquentielle" , et le signal clk est l'horloge à ce moment. Remarque : Pour déterminer si un signal est une horloge, il ne faut pas regarder le nom, mais regarder où le signal est placé. Seules celles qui sont placées dans la liste sensible et qui sont déclenchées par front sont des horloges. Le signal rst_n est un signal de réinitialisation, et il n'est pas jugé par le nom, mais placé dans la liste sensible et déclenché par le même front. Le plus important est que "l'instruction du programme" juge d'abord la valeur de rst_n, ce qui signifie que rst_n a la priorité la plus élevée. Les deux sont utilisés pour la réinitialisation.

Les points suivants doivent être pris en compte lors de la conception :

1. * Les variables sensibles dans l'instruction always de la logique combinatoire doivent être écrites en toutes lettres ou remplacées par " " .

2. L'affectation des dispositifs logiques combinatoires adopte l'affectation bloquante "=", la déclaration d'affectation des dispositifs logiques séquentiels adopte l'affectation non bloquante "<=" ,

Voir la section "Affectations bloquantes et non bloquantes" pour des raisons spécifiques.

5.2 Système de numérotation

5.2.1 Représentation numérique

Le format le plus couramment utilisé pour la représentation numérique dans Verilog est : <bit width>'<radix><value> , tel que 4'b1011. Bit Width : Un entier décimal décrivant le nombre de bits contenus dans la constante, qui est facultatif. Par exemple, le 4 dans 4'b1011 est la largeur de bit, qui est de 4 fils dans la compréhension populaire. S'il n'y a pas un tel élément, il peut être déduit de la valeur de la constante. Par exemple, 'b1011 déduit une largeur de bit de 4, tandis que 'b10010 déduit une largeur de bit de 5.

Radix : Indique le nombre de bases de la valeur. Peut être b, B, d, D, o, O, h ou H pour binaire, décimal, octal et hexadécimal, respectivement . Sans cette entrée, la valeur par défaut est un nombre décimal. Par exemple, 4'b1011 en binaire peut s'écrire 4'd11 en décimal, 4'hb en hexadécimal ou 4'o13 en octal, ou 11 sans la base. Pour résumer, tant que le nombre binaire est le même, c'est le même nombre qu'il soit écrit en décimal, octal ou hexadécimal.

Valeur : Une chaîne de codes ASCII représentant la valeur réelle d'une constante déterminée par la base. Si la base est définie comme b ou B, la valeur peut être 0, 1, x, X, z ou Z. Si la base est définie comme o ou O, les valeurs peuvent être 2, 3, 4, 5, 6, 7. Si la base est définie comme h ou H, les valeurs peuvent être 8, 9, a, b, c, d, e, f, A, B, C, D, E, F. Pour la base d ou D, le signe de la valeur peut être n'importe quel nombre décimal : 0 à 9, mais pas x ou z. Par exemple, 4'b12 est faux, car b signifie binaire et la valeur ne peut être que 0, 1, x ou z, sans compter 2. 32'h12 est égal à 32'h00000012, c'est-à-dire que lorsque la valeur n'est pas écrite complètement, le bit haut est rempli avec 0.

5.2.2 Le binaire est la base

Dans un circuit numérique, si la puce A transmet des données à la puce B, telles que la transmission d'informations 0 ou 1, la puce A et la puce B peuvent être connectées via une broche, puis la puce A contrôle la sortie de la broche pour qu'elle soit haute ou basse. Niveau, 0 et 1 sont représentés par des niveaux haut et bas. Lorsque la puce B détecte que la broche est au niveau bas, cela signifie qu'elle a reçu 0, et lorsque la puce B détecte que cette broche est au niveau haut, cela signifie qu'elle a reçu 1.

image-20211029114040717

A l'inverse, si un niveau bas est utilisé pour indiquer que 1 est reçu, et un niveau haut est utilisé pour indiquer que 0 est reçu, est-ce possible ? Bien sûr, tant que la puce A et la puce B sont d'accord à l'avance, lorsque la puce A veut envoyer un 1 numérique, elle mettra la broche au niveau bas. La puce B détecte que la broche est au niveau bas, indiquant que le 1 numérique est reçu et que la communication est terminée.

image-20211029114120140

Une broche a deux états haut et bas, qui peuvent représenter respectivement deux situations de 0 et 1 numériques. Et si la puce A veut envoyer les numéros 0, 1, 2, 3 à la puce B ?

Vous pouvez connecter la puce A et la puce B avec deux broches, c'est-à-dire deux lignes : a et b. Lorsque les deux lignes sont de bas niveau, cela signifie envoyer le numérique 0 ; lorsque a est de haut niveau et b est de bas niveau, cela signifie envoyer le numérique 1 ; quand a est de bas niveau et b est de haut niveau, cela signifie envoyer le numérique 2 ; lorsque Lorsque les deux les lignes sont hautes, cela signifie envoyer le numéro 3.

image-20211029114157781

Selon le même principe, lorsque la puce A veut envoyer les données 4, 5, 6, 7 à la puce B, il suffit d'ajouter une autre ligne. Les trois fils ont un total de 8 états, qui peuvent représenter 8 nombres. En résumé, différents états de niveau de la ligne peuvent représenter différentes significations, et autant d'états différents qu'il y en a peuvent représenter autant de nombres.

Réfléchissons-y si la puce A veut envoyer +1, -1, 0, +2 et d'autres nombres à la puce B, comment exprimer le positif et le négatif ici ? En se référant à l'idée précédente, la signification des niveaux haut et bas de la ligne est convenue à l'avance par les deux côtés de la puce. Dans ce cas, une seule ligne peut être utilisée pour représenter le symbole, par exemple, niveau bas signifie nombre positif, et haut niveau signifie nombre négatif.

image-20211029114327334

Parmi les trois lignes illustrées dans la figure ci-dessus, la ligne c est utilisée pour représenter le positif et le négatif, où 0 représente un nombre positif et 1 représente un nombre négatif. Utilisez la ligne a et la ligne b pour représenter la valeur, en prenant 3'b111 comme exemple, il peut être interprété comme le nombre décimal 7, il peut également être interprété comme le numéro signé code d'origine "-3", et il peut aussi être interprété comme le complément de nombre signé "-1 , la façon dont il est interprété dépend de la définition de l'ingénieur d'un nombre binaire. Tant que cette définition n'affecte pas la communication entre les circuits, aucun problème ne surviendra. Par conséquent, "0" et "1" dans les nombres peuvent non seulement représenter des significations numériques littérales, mais également représenter d'autres significations, telles que des symboles positifs et négatifs, etc. De la même manière, dans les circuits numériques, les nombres binaires sont à la base d'autres systèmes de nombres tels que les nombres octaux, décimaux, hexadécimaux, signés, non signés et décimaux. Dans la conception de FPGA, la raison la plus fondamentale de ne pas connaître les méthodes de calcul des nombres décimaux et signés est de ne pas connaître les valeurs binaires correspondant à ces données. Tant que vous comprenez les valeurs binaires correspondantes, de nombreux problèmes peuvent être résolus.

Laissez les étudiants mieux comprendre ce concept à travers les exemples ci-dessous.Beaucoup de débutants demandent souvent, comment réaliser le calcul décimal dans FPGA? Prenons "0,5+0,25" comme exemple. Il est bien connu que le résultat de 0,5+0,25 est 0,75. Considérez comment exprimer 0,5, 0,25 et 0,75 en binaire ? La méthode de représentation spécifique dépend de la pratique de l'ingénieur, car il existe de nombreuses méthodes de représentation de ce type, telles que les décimales à virgule fixe, les décimales à virgule flottante et même, comme indiqué ci-dessus, en utilisant quelques lignes à définir par vous-même. peut être normal, il n'y a pas de problème. Supposons qu'un ingénieur utilise trois fils pour définir la valeur décimale représentée par la valeur binaire, comme indiqué dans le tableau ci-dessous.

valeur binaire définition valeur binaire définition
3'b000 0,1 3'b100 0,25
3'b001 0,5 3'b101 0,3
3'b010 0,75 3'b110 0,8
3'b011 0,2 3'b111 0

Afin d'illustrer que la signification des valeurs binaires peut être librement définie, l'ordre des nombres est dans le désordre. Alors pourquoi seulement ces types de nombres décimaux ? C'est parce que le système supposé n'a que ces types de nombres, et si vous voulez représenter plus de nombres, vous pouvez augmenter le nombre de lignes. Après avoir complété la définition ci-dessus, il est très facile de réaliser "0.5+0.25", qui est en fait "l'addition" de 3'b001 et 3'b100, en espérant obtenir 3'b010. Mais en utilisant directement 3'b001 + 3'b100 dans le tableau, le résultat est "101", ce qui n'est pas le résultat souhaité, et le code peut être écrit comme suit :

image-20211029114740441

Bien sûr, ce n'est qu'une manière d'écrire, tant que la fonction correspondante peut être réalisée et que le résultat est correct, n'importe quelle manière d'écrire convient.

Il peut y avoir des doutes ici, 0,1 + 0,8 devrait être 0,9, mais il n'y a pas de représentation de 0,9 dans le tableau ci-dessus. Il s'agit en fait d'un défaut dans la table définie par le concepteur, ou le concepteur pense que cette situation ne se produira pas. Ce que je veux exprimer ici est le suivant : tant que les nombres binaires correspondants sont définis, de nombreuses fonctions sont faciles à concevoir.

Bien sûr, dans l'ingénierie proprement dite, les pratiques convenues et habituelles sont généralement suivies, et il n'est pas nécessaire de trouver un autre moyen. Par exemple, le tableau suivant est la définition des décimales à virgule fixe couramment utilisées :

valeur binaire définition valeur binaire définition
3'b000 0.0 3'b100 0,5
3'b001 0,125 3'b101 0,625
3'b010 0,25 3'b110 0,75
3'b011 0,3725 3'b111 0,8725

A ce moment, si vous voulez réaliser 0+0.5=0.5, c'est-à-dire ajouter 3'b000 et 3'b100, attendez-vous à obtenir 3'b100. On peut trouver que 3'b100 peut être obtenu en utilisant directement le binaire 3'b000+3'b100. De même, pour obtenir 0,125+0,75=0,8725, c'est-à-dire ajouter 3'b001 et 3'b110, attendez-vous à obtenir 3'b111. On peut trouver que 3'b111 peut être obtenu en utilisant directement le binaire 3'b001+3'b110.

Si le calcul de 0,5 + 0,75 = 1,25 doit être réalisé, on peut voir que 1,25 a dépassé la plage de représentation à ce moment, et ce problème peut être résolu en augmentant la largeur en bits du signal ou en ne représentant que des décimales. Si seules les décimales sont représentées, le résultat est 0,25, c'est-à-dire en ajoutant 3'b100 et 3'b110, en s'attendant à obtenir 3'b010. Il n'est pas difficile de trouver que 3'b100 + 3'b110 = 4'b1010, exprimé en 3 bits est 3'b010, soit 0,25. D'après ce qui précède, on peut voir que le calcul des décimales à virgule fixe n'est pas compliqué et que le calcul peut être effectué directement après avoir défini la relation entre les décimales à virgule fixe et les valeurs binaires.

5.2.3 États indéterminés

Comme mentionné ci-dessus, les circuits numériques n'ont que des niveaux haut et bas, qui représentent respectivement 1 et 0. Mais x et z peuvent souvent être vus dans le code, comme 1'bx, 1'bz. Alors, quels sont les niveaux de x et z ? La réponse est qu'il n'y a pas de niveau réel correspondant aux deux. x et z sont davantage l'intention d'un concepteur ou à des fins de simulation, pour indiquer aux simulateurs et aux synthétiseurs comment interpréter ce code.

L'état X est appelé un état indéterminé, qui est souvent utilisé pour juger de la condition, afin de dire au concepteur de l'outil de synthèse qu'il ne se soucie pas de son niveau, que ce soit 0 ou 1, c'est bien.

image-20211029114938488

Dans l'exemple ci-dessus, on peut voir que la condition de jugement est din== 4'b10x0, ce qui équivaut à din== 4'b1000||din==4'b1010, où "||" est un "ou" symbole.

image-20211029115013142

Cependant, il est préférable d'écrire directement din== 4'b1000||din == 4'b1010 que "din == 4'b10x0" dans le dessin, car cette manière d'écrire est plus directe et simple.

Dans le processus de simulation, certains signaux ont un état indéterminé, de sorte que le concepteur doit soigneusement analyser si l'état indéfini est raisonnable. Si vous ne vous souciez vraiment pas de savoir si c'est 0 ou 1, alors vous pouvez le laisser seul. Mais il est recommandé que tous les signaux ne soient pas dans un état indéterminé, écrivez clairement s'il s'agit de 0 ou 1 et n'ajoutez pas de problèmes de "réflexion" à la conception.

5.2.4 État haute impédance

L'état Z, généralement appelé état à haute impédance, signifie que le concepteur ne pilote pas ce signal (ni 0 ni 1) et est généralement utilisé dans l'interface de porte à trois états.

image-20211030112458042

La figure ci-dessus est un cas d'application du bus à trois états. Le bus de connexion de la figure est à la fois une entrée et une sortie pour CPU et FPGA, et il s'agit d'une interface bidirectionnelle. Dans les circuits matériels généraux, une résistance de pull-up (faible pull-up) ou une résistance de pull-down (faible pull-down) sera connectée à cette ligne.

Le point A reste haut lorsque ni le CPU ni le FPGA ne pilotent le bus. Lorsque le FPGA ne pilote pas le bus et que le CPU pilote le bus, la valeur du point A est déterminée par le CPU. Lorsque le CPU ne pilote pas le bus et que le FPGA pilote le bus, la valeur du point A est déterminée par le FPGA. Mais FPGA et CPU ne peuvent pas piloter le bus en même temps, sinon le niveau de A sera incertain. Habituellement, lorsque FPGA et CPU pilotent le bus, ils fonctionnent selon l'accord négocié à l'avance.

image-20211030112556798

La figure ci-dessus est une synchronisation I2C typique. Le bus SDA de I2C est un signal à trois états. Le protocole I2C a stipulé quelle heure est pilotée par l'appareil maître et quelle heure est pilotée par l'appareil esclave dans l'heure ci-dessus.Les deux parties doivent respecter l'accord et ne peuvent pas être pilotées en même temps. Alors, comment le FPGA atteint-il le comportement de "ne pas piloter" dans la conception ? C'est parce qu'il y a des portes à trois états à l'intérieur du FPGA.

image-20211030112615735

La porte à trois états est un élément matériel, et la figure ci-dessus est sa structure typique. La porte à trois états a quatre interfaces, telles que l'activation d'écriture wr_en, l'écriture de données wr_data, la lecture de données rd_data et les données de signal à trois états connectées à des périphériques externes, comme indiqué dans la figure ci-dessus.

Il convient de noter que le signal d'activation d'écriture, lorsque le signal est valide, la porte à trois états attribuera la valeur de wr_data aux données de ligne à trois états, à ce moment, la valeur des données est déterminée par wr_data, lorsque wr_data est 0, la valeur de data est 0 ; lorsque wr_data Lorsqu'il est 1, la valeur de data est 1. Lorsque le signal d'activation d'écriture est invalide, quelle que soit la valeur wr_data, cela n'affectera pas la valeur des données externes, c'est-à-dire qu'il ne sera pas piloté.

Dans Verilog, les fonctions ci-dessus sont réalisées par le code suivant :

image-20211030112644309

Lorsque le synthétiseur voit ces deux lignes de code, il sait qu'il va être synthétisé en une porte à trois états, et c'est le rôle de la haute impédance z. De plus, on peut remarquer que l'utilisation de lignes à trois états sur le matériel consiste à réduire les broches et qu'il n'est pas nécessaire de réduire le câblage dans le FPGA, il est donc inutile d'utiliser des signaux à trois états. Par conséquent, il est recommandé de ne pas utiliser l'état "z" à haute impédance à l'intérieur du FPGA lors de la conception, car il n'est pas nécessaire de s'ajouter des problèmes de "réflexion". Bien sûr, si l'état à haute impédance est utilisé dans la conception, aucune erreur ne sera signalée et la fonction peut également être réalisée.

D'une manière générale, l'état de haute impédance "z" signifie le comportement de "ne pas piloter le bus". En fait, les circuits numériques sont hauts ou bas, et il n'y a pas d'autres niveaux.

5.3 Opérateurs arithmétiques

image-20211030112732563

image-20211030145125938

Les opérateurs arithmétiques incluent l'addition "+", la soustraction "-", la multiplication "*", la division "/" et le reste "%". Les opérateurs arithmétiques couramment utilisés incluent principalement : l'addition "+", la soustraction "-" et la multiplication "*" .

Notez que les opérations couramment utilisées n'incluent pas les opérateurs de division et de reste, car les opérateurs de division et de reste ne sont pas construits avec une logique de porte simple et que les circuits matériels correspondants sont relativement volumineux. L'addition et la soustraction sont les opérations les plus simples, et la multiplication peut être désassemblée en plusieurs opérations d'addition, de sorte que les circuits correspondant à l'addition, la soustraction et la multiplication sont relativement petits. La division est différente, les élèves peuvent rappeler les étapes de la division, qui implique de multiples multiplications, décalages, additions et soustractions, donc le circuit correspondant à la division est compliqué, ce qui oblige également le concepteur à être prudent lors de la conception de Verilog. avertir.

5.3.1 Opérateur d'addition

Apprenez d'abord l'opérateur d'addition, le symbole "+" peut être utilisé directement dans le code Verilog :

image-20211030112934610

Son schéma électrique est le suivant :

image-20211030112947325

Un synthétiseur peut reconnaître l'opérateur d'addition et le transformer en un circuit comme celui illustré ci-dessus. L'opération d'addition binaire est similaire à l'opération d'addition décimale, le système décimal est tous les dix et le binaire est tous les deux. L'opération de base de l'addition binaire est la suivante :

image-20211030113004606

5.3.2 Opérateur de soustraction

Opérateur de soustraction, le symbole "-" peut être utilisé directement dans le code Verilog :

image-20211030113035316

Son schéma électrique est le suivant :

image-20211030113049315

Un synthétiseur peut reconnaître l'opérateur de soustraction et le transformer directement dans le circuit illustré ci-dessus.

L'opération de soustraction binaire est similaire à l'opération de soustraction décimale, et il existe également le concept d'emprunt. En système décimal, un est emprunté comme dix, et en binaire, un est emprunté comme deux. Le fonctionnement de base de la soustraction 1 bit est le suivant :

image-20211030113108106

5.3.3 Opérateur de multiplication

Opérateur de multiplication, le symbole "*" peut être utilisé directement dans le code Verilog :

image-20211030113141837

Son schéma électrique est le suivant :

image-20211030113153342

Le synthétiseur reconnaît l'opérateur de multiplication et le transforme directement dans le circuit illustré ci-dessus. L'opération de multiplication binaire est similaire à l'opération de multiplication décimale, et le processus de calcul est le même. L'opération de base de la multiplication 1 bit est la suivante :

image-20211030113213925

La multiplication entre plusieurs chiffres est identique au processus de calcul décimal. Par exemple, le processus de calcul de 2'b11 * 3'b101 est le suivant :

image-20211030113232235

5.3.4 Opérateurs de division et de reste

L'opérateur de division peut utiliser le symbole "/" directement dans le code Verilog, tandis que l'opérateur de reste est "%":

image-20211030142720756

Le schéma de principe du circuit de division est le suivant :

image-20211030142733602

Le schéma de principe du circuit restant est le suivant :

image-20211030142749890

Le synthétiseur peut reconnaître l'opérateur de division et l'opérateur de reste, mais ces deux opérateurs incluent un grand nombre d'opérations de multiplication, d'addition et de soustraction, de sorte que le circuit du diviseur dans le FPGA est très grand et que le synthétiseur peut ne pas être directement converti en circuit représenté sur la fig.

Il peut y avoir des doutes ici : pourquoi la division et le reste prennent-ils beaucoup de ressources ? Analysons le processus de division décimale et de reste, en prenant 122 divisé par 11 comme exemple.

image-20211030142812957

Dans le processus d'exécution des opérations ci-dessus, plusieurs décalages, multiplications, soustractions et autres opérations sont impliqués. C'est-à-dire que plusieurs multiplicateurs et soustracteurs sont utilisés pour effectuer une opération de division, ce qui nécessite des ressources matérielles relativement importantes. Il en va de même pour les opérations binaires.

Par conséquent, dans le code de conception, la division et le reste ne sont généralement pas utilisés. Il existe différentes façons d'éviter les opérations de division et de reste dans l'algorithme. Par conséquent, dans le traitement numérique du signal, la communication et le traitement d'images, vous trouverez beaucoup de multiplications, d'additions et de soustractions, etc., mais vous verrez rarement des opérations de division et de reste. Mais dans le test de simulation, la division et le reste peuvent être utilisés, car ils ne sont utilisés que pour le test de simulation et n'ont pas besoin d'être synthétisés dans un circuit, donc naturellement il n'y a pas besoin de se soucier du nombre de ressources occupées.

5.3.5 Résumé de l'expérience

problème de largeur de bit

Lors de l'écriture du code, vous devez faire attention à la largeur en bits du signal. Le résultat final dépend de la largeur en bits du signal à gauche du signe "=", enregistrez le bit bas et supprimez le bit haut. Par exemple:

image-20211030142922330

La largeur en bits du signal c est de 1 bit, donc le résultat de l'opération réserve finalement le 1 bit le plus bas, donc la valeur de c est 1'b0. Étant donné que la largeur en bits de d est de 2 bits, les 2 bits inférieurs du résultat de l'opération peuvent être réservés, de sorte que la valeur de d est 2'b10. Étant donné que la largeur en bits de e est de 3 bits, les 3 bits inférieurs du résultat de l'opération peuvent être réservés, de sorte que la valeur de e est 3'b010. "1" est de 32 bits par défaut, et le résultat de 1+1 est également de 32 bits, mais comme la largeur en bits de f n'est que de 3 bits, les 3 bits inférieurs du résultat de l'opération peuvent être réservés, de sorte que la valeur de f est 3'b010.

Il en va de même pour les opérations de soustraction. Prenons le code suivant comme exemple :

image-20211030142952611

La valeur binaire obtenue à partir de "0-1" est "1111111111….", mais le résultat enregistré dépend de la largeur en bits du signal à gauche du signe "=". La largeur de bit de c est 1, et le bit 1 le plus bas est réservé, donc la valeur de c est 1'b1. Puisque la largeur en bits de d est de 2 bits, les 2 bits inférieurs sont réservés dans le résultat, donc la valeur de d est 2'b11. Comme la largeur en bits de e est de 3 bits, les 3 bits inférieurs sont réservés dans le résultat, donc la valeur de e est 3'b111. La largeur en bits de f est de 4 bits, donc les 4 bits inférieurs du résultat de l'opération peuvent être réservés, donc la valeur de f est 4'b1111.

Lors de l'écriture du code de multiplication, vous devez également faire attention à la largeur en bits du signal. Le résultat final dépend de la largeur en bits du signal à gauche du signe "*". Enregistrez le bit bas et supprimez le bit haut :

image-20211030143016965

La valeur binaire obtenue par "2'b11 * 3'b101" est "4'b1111", mais le résultat enregistré dépend de la largeur en bits du signal à gauche du "*". La largeur de bit de c est 1, et le bit 1 le plus bas est réservé, donc la valeur de c est 1'b1. Puisque la largeur en bits de d est de 2 bits, les 2 bits inférieurs sont réservés dans le résultat, donc la valeur de d est 2'b11. Comme la largeur en bits de e est de 3 bits, les 3 bits inférieurs sont réservés dans le résultat, donc la valeur de e est 3'b111. La largeur en bits de f est de 4 bits, donc les 4 bits inférieurs du résultat de l'opération peuvent être réservés, donc la valeur de f est 4'b1111. Il convient de noter que h, le signal a 5 bits, 4'b1111 est affecté au signal 5 bits, et le résultat est que les bits hauts sont remplis de 0, donc le résultat est 5'b01111.

L'origine du complément

Lorsque FPGA implémente divers algorithmes, le plus important est de garantir l'exactitude des résultats de calcul, sinon tout n'a pas de sens. Lors de l'analyse des opérateurs d'addition et de soustraction, on peut constater que le fait que la largeur de bit du signal pour enregistrer le résultat soit raisonnable a une grande influence sur l'exactitude.

Par exemple l'opération d'addition suivante :

image-20211030143139276

On peut voir à partir du tableau ci-dessus que si le report n'est pas conservé, le résultat du calcul est incorrect lorsque l'addition se produit, et le résultat du calcul n'est correct que si le report est conservé. À partir de là, nous pouvons tirer une conclusion: lors de l'utilisation de l'addition, afin de garantir l'exactitude du résultat, le report doit être enregistré, c'est-à-dire que le résultat doit étendre la largeur en bits.

Par exemple, lors de l'ajout de deux nombres de 8 bits, le résultat doit être étendu d'un bit et la largeur de bit est définie sur 9 bits.

image-20211030143209482

Ensuite, analysons l'opération de soustraction, comme indiqué dans le tableau suivant :

image-20211030143329162

Notez que dans le tableau et 2'b00-2'b01, le résultat est 2'b11, la valeur décimale correspondante est 3, mais le résultat attendu est "-1". De la même manière, 2'b01 - 2'b11, le résultat est 2'b10, la valeur décimale correspondante est 2 et le résultat attendu est "-2", donc le résultat ci-dessus est incorrect.

Lorsque le résultat attendu est positif ou négatif, un bit de signe peut être ajouté pour distinguer le résultat positif ou négatif. La méthode de représentation convenue dans l'industrie est que lorsque le bit le plus élevé est 0, cela signifie un nombre positif, et lorsque le bit le plus élevé est 1, cela signifie un nombre négatif. La valeur après le bit de signe est représentée par les 2 bits inférieurs et le résultat est le suivant :

image-20211030143414060

On peut voir dans le tableau ci-dessus qu'après avoir ajouté le bit de signe, il y aura encore des problèmes que les résultats de l'opération ne répondent pas aux attentes. Par exemple, 2'b00-2'b01 dans le tableau, le résultat est 3'b111, la valeur décimale correspondante est -3, mais le résultat attendu est "-1". Le résultat ci-dessus est donc toujours incorrect.

Maintenant, reconvertissez le nombre binaire "000~111" comme suit :

a. Nombre positif : reste inchangé

b. Nombre négatif : le bit de signe reste inchangé, la valeur est inversée et 1 est ajouté .

C'est-à-dire que s'il s'agit d'un nombre positif "+1", il était représenté par "001" auparavant, mais il est toujours représenté par "001" maintenant. S'il s'agit d'un nombre négatif "-1", il était représenté par "101" auparavant, mais maintenant il est représenté par "111". Le nombre négatif "-3" était auparavant représenté par "111", mais maintenant il est représenté par "101". Cette représentation est la représentation complémentaire .

Après l'avoir exprimé en code complémentaire, analysons le résultat :

image-20211030145353945

On peut voir que les résultats du tableau ci-dessus sont tous corrects et conformes aux attentes. Ce processus n'a apporté aucune modification au code, mais le résultat correct a été obtenu en modifiant la définition des données.

Dans la discussion précédente, les opérations d'addition, de sommation, de soustraction et de diminution de fin n'utilisaient pas de nombres signés. Maintenant, re-représentez-le en utilisant le complément à deux du nombre signé. En supposant que l'addend, le summand, le soustrahend et le minuend sont tous 2 bits (la plage est de -2 ~ 1), compte tenu de la raison du report et de l'emprunt, le résultat est représenté par 3 bits (la plage est de -4 ~ 3 ). Étant donné que la largeur en bits du résultat devient de 3 bits, la soustraction et la diminution de la fin sont développées pour être représentées par 3 bits, comme indiqué dans le tableau suivant :

image-20211030145455123

image-20211030145507551

Les étapes de l'opération récapitulative sont les suivantes :

  1. Selon le "bon sens humain", les valeurs maximale et minimale du résultat sont censées déterminer la largeur en bits du signal du résultat .
  2. Étendez la largeur en bits de l'addition, de la soustraction et d'autres données pour rendre la largeur en bits du résultat cohérente.
  3. Effectuer des calculs en addition et soustraction binaires.

Grâce à la méthode ci-dessus, ce qui est obtenu est le résultat du code complémentaire. En fait, dans les FPGA et même les systèmes informatiques, toutes les données sont stockées sous forme complémentaire . Si vous souhaitez en savoir plus sur le code complémentaire, vous pouvez vous référer aux documents connexes.

5.4 Opérateurs logiques

image-20211030145547059

image-20211030145556624

Il existe 3 opérateurs logiques dans le langage Verilog HDL, ce sont :

(1) && : logique et ;

(2) | | : ou logique ;

(3) ! : NON logique .

5.4.1 ET logique

"&&" est un opérateur binaire, qui nécessite deux opérandes, comme a && b.

(1) ET logique 1 bit

image-20211105212729170

Lorsque A et B valent 1, C vaut 1, sinon C vaut 0.

Le schéma de circuit matériel correspondant est le suivant :

image-20211105212800939

(2) ET logique multi-bits

image-20211105212816535

C vaut 1 quand ni A ni B ne vaut 0, sinon c'est 0.

image-20211105212839123

5.4.2 OU logique

"||" est un opérateur binaire, qui nécessite deux opérandes , comme a||b.
(1) OU logique 1 bit

image-20211105213147969

L'un de A et B vaut 1, C vaut 1, sinon C vaut 0.

Le schéma de circuit matériel correspondant est illustré dans la figure ci-dessous :

image-20211105213306687

(2) OU logique multi-bits

image-20211105213322593

Si l'un de A et B est non nul, C vaut 1, sinon C vaut 0.

Le schéma de circuit matériel correspondant est illustré dans la figure ci-dessous :

image-20211105213347308

5.4.3 NON logique

"!" est un opérateur unaire, qui ne nécessite qu'un seul opérande, tel que ! (a>b).

image-20211105213442864

Pour l'opérande a, il faut juger si a est vrai, s'il est vrai, exécuter l'opération à l'intérieur de {}, et s'il est faux, terminer l'opération.
Le tableau suivant est la table de vérité des opérations logiques, qui indique les valeurs obtenues par diverses opérations logiques lorsque les valeurs de a et b sont dans des combinaisons différentes.

image-20211105213506720

Le résultat final des opérateurs logiques est seulement logiquement vrai ou logiquement faux, c'est-à-dire 1 ou 0. En général, lorsque des opérateurs logiques sont utilisés comme conditions de jugement, l'opération logique ET ne peut être que deux nombres de 1 bit de large. Ce n'est que lorsque deux expressions sont vraies en même temps qu'elle peut être vraie, et si l'une d'entre elles est fausse, ça peut être faux.

Si l'opérande est multibit, l'opérande peut être considéré comme un tout. Si chaque bit de l'opérande est 0, c'est une valeur logique 0 ; s'il y a un
1 dans l'opérande, c'est une valeur logique 1.

image-20211105213540379

Puisque ni 4'b0111 ni 4'b1000 n'est 0, il est considéré comme logiquement vrai s'il n'est pas 0, donc le code ci-dessus est équivalent au code suivant.

image-20211105213557611

Autrement dit, le résultat est que a est logiquement vrai, b est logiquement vrai et c est logiquement faux.

5.4.4 Résumé de l'expérience

(1) Priorité des opérateurs logiques

Parmi les opérateurs logiques - "&&" et - "||" ont une priorité inférieure aux opérateurs arithmétiques ; "!" a une priorité plus élevée que les opérateurs logiques binoculaires

Les exemples sont les suivants :

image-20211105213736040

(2) Les deux côtés de l'opérateur logique correspondent à des signaux 1 bit

Expérience :Les opérateurs logiques des deux côtés correspondent à des signaux 1 bit

image-20211105213859092

Faites attention au code ci-dessus, où a et b sont tous deux des signaux multi-bits, indiquant que deux signaux multi-bits sont logiquement AND. La compréhension correcte de ce code est la suivante : lorsque a n'est pas égal à 0 et b n'est pas égal à 0, la valeur de d est 1. Cependant, même les ingénieurs ayant de nombreuses années d'expérience professionnelle peuvent difficilement comprendre intuitivement la signification implicite du code ci-dessus. Le concept qui n'est pas égal à 0 signifie logiquement vrai et égal à 0 signifie logiquement faux est facilement négligé.

Par conséquent, bien qu'il n'y ait pas d'erreur dans le code ci-dessus, le concepteur ne doit pas écrire le code dans le but de montrer la technologie dans la conception, et cette façon d'écrire est sujette à une mauvaise conception, par exemple, elle peut à l'origine exprimer assign d = a & b, mais au final, le code ci-dessus a été écrit car la conception n'était pas assez intuitive, ce qui a entraîné des problèmes de conception. Par conséquent, il est très important d'écrire de manière intuitive et compréhensible dans la conception, afin que vous et les autres puissiez comprendre immédiatement la signification du code lorsqu'ils voient le code, il est donc recommandé d'écrire le code ci-dessus sous la forme suivante.

image-20211105213942868

(3) Parenthèses multi-usages pour distinguer les priorités

Expérience 2 :N'essayez pas de vous souvenir de la priorité, utilisez plutôt des parenthèses

En fait, les ingénieurs ne se souviennent pas de toutes les priorités dans leur travail, et se souvenir de toutes les priorités n'améliorera pas beaucoup l'efficacité du travail des ingénieurs. Dans la conception, vous pouvez rencontrer le code suivant :

(1) a < b && c > d ;

(2) une = = b | | c = = ré ;

(3)! un | | un > b 。

Si vous ne vous souvenez pas de la priorité des opérateurs, lorsque vous rencontrerez des situations comme ces trois exemples, vous passerez certainement un certain temps à trier vos pensées et à réfléchir à la partie à juger en premier. Si l'ingénieur peut se souvenir de la priorité, il doit également communiquer et vérifier le travail de ces codes, et les êtres humains sont enclins à faire des erreurs, et ces erreurs sont souvent négligées et difficiles à vérifier.

Par conséquent, afin d'améliorer la lisibilité du programme et d'exprimer clairement la relation de priorité entre les opérateurs, il est recommandé d'utiliser davantage de crochets dans la conception. Les trois exemples ci-dessus peuvent s'écrire :

1)( a < b) &&( c ​​> d);

2)( un = = b) | |(c = = ré);

3)(!a) | |(a > b)。

(4) Utiliser NOT moins logique

Expérience 3 :Utilisez "logique et" et "logique ou" plus, et utilisez moins de logique non

« Et logique » traduit en chinois est « et », et « ou logique » traduit en chinois est « ou ». Supposons qu'il y ait un indicateur de signal, 0 signifie inactif, 1 signifie occupé. "(!(flag== 0) && a== 4' b1000)", qui se lit comme "prendre l'état opposé lorsqu'il est inactif, et la condition est vraie lorsque a est égal à 4' b1000". C'est très difficile à lire, et lors de la lecture de ce code, vous devez tourner un peu plus la tête et y réfléchir un peu plus. Afin de rendre le code plus intuitif, il est suggéré que l'exemple ci-dessus soit écrit comme "flag== 1 && a==4' b1000", qui se lit comme "quand occupé et a est égal à 4' b1000" la condition est vrai.

5.5 Opérateurs logiques au niveau du bit

image-20211105215609897

image-20211105215735525

Remarque : ~ ^, ^ ~ (binaire XOR, NOR) : (équivalent à l'opération NOR).

Il existe les opérateurs binaires suivants dans le langage Verilog HDL :

~ (unaire NOT): (équivalent à l'opération NOT)

& (ET binaire): (équivalent à l'opération ET)

| (ou binaire): (équivalent à l'opération OU)

^ (XOR binaire): (équivalent à l'opération XOR)

Ces opérateurs opèrent au niveau du bit sur les bits correspondants des opérandes d'entrée et produisent un résultat vectoriel. Chaque table de vérité dans la figure ci-dessous montre le résultat d'une opération au niveau du bit pour différents opérateurs logiques au niveau du bit :

image-20211106195003983

5.5.1 ET bit à bit monoculaire

L'opérateur ET unaire au niveau du bit &, après l'opérateur, est le signal qui doit être exploité logiquement, ce qui signifie l'opération ET entre chaque bit du signal. Par exemple

Rég[3:0] A,C ;

affecter C=&A ;

Le code ci-dessus est équivalent à C = A[3] & A[2] & A[1] & A[0] ; si A=4'b0110, le résultat de C est 0.

5.5.2 OU bit à bit monoculaire

Opérateur OR bit à bit monoculaire |, après l'opérateur est le signal qui doit être actionné logiquement, indiquant que le signal est ORed entre les bits. Par exemple

reg[3:0] A, C;

affecter C=|A ;

Le code ci-dessus est équivalent à C = A[3] | A[2] | A[1] | A[0] ; si A=4'b0110, le résultat de C est 1.

5.5.4 ET bit à bit binoculaire

Opérateur binoculaire ET au niveau du bit &, le signal est situé sur les côtés gauche et droit de l'opérateur, ce qui signifie que l'opération ET de phase correspondante est effectuée sur les deux signaux. Par exemple

reg[3:0] A,B,C;

attribuer C = A & B ;

Le code ci-dessus est équivalent à : C[0] = A[0] & B[0], C[1] = A[1] & B[1], C[2] = A[2] & B[2 ], C[3] = A[3] & B[3]. Si A=4'b0110, B=4'b1010, le résultat de C est 4'b0010.

Si les longueurs des opérandes ne sont pas égales, l'opérande avec la plus petite longueur est complété par 0 sur le côté le plus à gauche. Par exemple,

reg[1:0] A ;

reg[2:0] B ;

reg[3:0] C;

attribuer C = A & B ;

Le code ci-dessus est équivalent à : C[0] = A[0] & B[0], C[1] = A[1] & B[1], C[2] = 0& B[2], C[ 3] = 0 &0.

5.5.5 OU bit à bit binoculaire

Opérateur OU bit à bit binoculaire |, le signal est situé sur les côtés gauche et droit de l'opérateur, indiquant que l'opération OU de phase correspondante est effectuée sur les deux signaux. Par exemple

reg[3:0] A, B, C;

attribuer C = A | B;

Le code ci-dessus est équivalent à : C[0] = A[0] | B[0], C[1] = A[1] | B[1], C[2] = A[2] | B[2 ], C[3] = A[3] | B[3]. Si A=4'b0110, B=4'b1010, le résultat de C est 4'b1110.

Si les longueurs des opérandes ne sont pas égales, l'opérande avec la plus petite longueur est complété par 0 sur le côté le plus à gauche. Par exemple,

reg[1:0] A ;

reg[2:0] B ;

reg[3:0] C;

attribuer C = A | B;

Le code ci-dessus est équivalent à : C[0] = A[0] | B[0], C[1] = A[1] | B[1], C[2] = 0 | B[2], C [3] = 0|0.

5.5.6 XOR bit à bit binoculaire

L'opérateur binoculaire XOR au niveau du bit ^, les signaux sont situés sur les côtés gauche et droit de l'opérateur, ce qui signifie qu'il faut effectuer l'opération XOR de phase correspondante sur les deux signaux. XOR fait référence à 0 0=0,1 1=0,0^1=1, c'est-à-dire que le même est 0 et la différence est 1. Par exemple

reg[3:0] A, B, C;

attribuer C = A ^ B ;

Le code ci-dessus est équivalent à : C[0] = A[0] ^ B[0], C[1] = A[1] ^ B[1], C[2] = A[2] ^ B[2 ], C[3] = A[3]
^ B[3]. Si A=4'b0110, B=4'b1010, le résultat de C est 4'b1100.

Si les longueurs des opérandes ne sont pas égales, l'opérande avec la plus petite longueur est complété par 0 sur le côté le plus à gauche. Par exemple,

reg[1:0] A ;

reg[2:0] B ;

reg[3:0] C;

attribuer C = A | B;

Le code ci-dessus est équivalent à : C[0] = A[0] ^ B[0], C[1] = A[1] ^ B[1], C[2] = 0 ^ B[2], C [3] = 0^0.

5.5.7 Résumé de l'expérience

Différence entre les opérateurs logiques et les opérateurs au niveau du bit

Les opérateurs logiques incluent &&, ||, !, et les opérateurs au niveau du bit incluent &, |, ~ . Alors, quelle est la différence entre les opérateurs logiques et les opérateurs au niveau du bit ? En comparant le ET logique "&&" et le ET "&" au niveau du bit, on peut voir que le fonctionnement de l'opérateur ET logiqueIl n'y a que deux résultats : logiquement vrai ou logiquement faux, c'est-à-dire 1 ou 0 ; et "&" est un opérateur au niveau du bit,pour deux opérations de données de plusieurs bits. Pour les opérateurs au niveau du bit, deux nombres sont AND, OR ou NOT au niveau du bit.

image-20211107101136767

Le résultat de l'opération ci-dessus est : a=1'b1, b=1'b1, c=1'b0, d=4'b0000, e=4'b1111, f=4'b1000.

5.6 Opérateurs relationnels

image-20211107101351496

image-20211107101503199

Les opérateurs relationnels sont : > (supérieur à), < (inférieur à), >= (pas inférieur à), <= (pas supérieur à), == (logiquement égal) et ! = (logique non égal).

Les opérateurs relationnels prennent la valeur true (1) ou false (0). Si l'un des opérandes est x ou z, le résultat est x . Exemple : 23 > 45 : Le résultat est faux ( 0 ). 52 < 8'hxFF : Le résultat est x.

Si les opérandes sont de longueurs différentes, l'opérande le plus court est complété par des zéros dans la direction du bit le plus significatif (à gauche) . Par exemple : 'b1000 >= 'b01110 est équivalent à : 'b01000 >= 'b01110, qui est faux (0).

Dans la comparaison de l'égalité et de l'inégalité logiques, tant qu'un opérande contient x ou z, le résultat de la comparaison est inconnu (x). Par exemple, si Data = 'b11x0 ; Addr = 'b11x0 ; then Data == Addr, la comparaison le résultat est incertain, c'est-à-dire que le résultat est x.

5.7 Opérateurs de décalage

Il existe deux opérateurs de décalage dans Verilog HDL, à savoir "<<" (opérateur de décalage gauche) et ">>" (opérateur de décalage droit).

Ce qui suit décrit l'utilisation des deux respectivement :

image-20211107102901190

image-20211107102911492

5.7.1 Opérateur de décalage à gauche

Dans Verilog HDL, "-"<< représente l'opérateur de décalage vers la gauche. Son expression générale est :

A <<n;

Parmi eux, A représente l'opérande à décaler et n représente le nombre de bits à décaler à gauche. Le sens de cette expression est de décaler l'opérande A vers la gauche de n bits. L'opération de décalage vers la gauche est un décalage logique, et 0 doit être utilisé pour combler le vide décalé, c'est-à-dire que 0 est rempli dans les bits inférieurs. Pour décaler vers la gauche de n bits, il faut remplir n 0.

image-20211107103016873

Puisque le code ci-dessus est décalé à gauche de 2 bits, 2 zéros sont remplis dans les bits inférieurs, donc le résultat courant du code ci-dessus est : a = 4'b1100.
Il y a trois points à noter dans l'opération de décalage à gauche:
(1) L'opération de décalage à gauche ne consomme pas de ressources logiques, même la porte ET et la porte NON ne sont pas nécessaires, c'est juste la connexion des lignes .

image-20211107103040064

Le code ci-dessus décale le signal b vers la gauche de deux bits et l'affecte à c. Le circuit matériel correspondant est le suivant :

image-20211107103103978

(2) L'opération de décalage à gauche doit stocker le résultat en fonction de la largeur de bit

Vous avez peut-être vu les codes suivants pendant le processus d'apprentissage : 4'b1001<<1=4'b0010 et 4'b1001<<1=5'b10010

Pourquoi l'opérande est-il également 4'b1001, qui est décalé d'un bit vers la gauche, mais le résultat est 4'b0010 et 5'b10010 ? En effet, après l'opération de décalage vers la gauche, cela dépend du nombre de bits utilisés pour stocker le résultat.

image-20211107103702482

Dans le code ci-dessus, puisque a est de 4 bits, seuls 4 bits peuvent être enregistrés, donc b est décalé vers la gauche de 1 bit et affecté à 4 bits a, et le résultat après avoir rempli les bits décalés avec 0 est a = 4'b0010 ;

image-20211107103721433

Dans le code ci-dessus, puisque a est de 5 bits, il peut stocker 5 bits de résultats, donc b est décalé vers la gauche de 1 bit et affecté à 5 bits a, et le résultat est a = 5'b10010 après avoir rempli les bits décalés avec 0 ; ( 3**)
gauche L'opérande de l'opération de décalage peut être une constante ou un signal**. De même, le nombre de décalage, constant pour l'opération de décalage vers la gauche peut également être un signal.

image-20211107103747278

Dans le code ci-dessus, cnt est incrémenté de 1 à chaque horloge, et comme il s'agit de 3 bits, la valeur est 0~2. a est 4'b1 décalé vers la gauche de cnt bits. Lorsque cnt est égal à 0, décalage à gauche de 0 bit, a est égal à 4'b1 ; lorsque cnt est égal à 1, décalage à gauche de 1 bit, a est égal à 4'b10. Par analogie, chaque changement d'horloge de a est le suivant :

image-20211107103805477

Il convient de noter que lorsque le numéro de décalage est un signal, le circuit intégré n'est pas une simple connexion, et le sélecteur représenté sur la figure ci-dessous peut être intégré . Cependant, même ainsi, les ressources consommées par ce circuit matériel sont encore relativement faibles.

image-20211107103822414

5.7.2 Opérateur de décalage à droite

Dans Verilog HDL, utilisez ">>" pour représenter l'opérateur de décalage vers la droite. Son expression générale est :

A >>n;

Parmi eux, A représente l'opérande à décaler, et n représente le nombre de bits à décaler vers la droite. La signification de ce code est de décaler l'opérande A vers la droite de n bits.

Il y a trois points à noter dans l'opération de décalage à droite:
(1) L'opération de décalage vers la droite est un décalage logique, et 0 est nécessaire pour combler le vide décalé, c'est-à-dire pour remplir les bits de poids fort avec 0, et le nombre de 0 à remplir
dépend de enregistre le résultat .

image-20211107103914795

4'b0111 Le résultat du décalage vers la droite de deux bits est 2'b01. Puisque a a 6 bits, l'attribution de 2 bits à 6 bits nécessite d'ajouter 0 au bit haut, donc 4 0 doivent être ajoutés. Ainsi, le résultat de l'exécution du code ci-dessus est :

a = 6'b0001
(2) Semblable à l'opération de décalage à gauche, l'opération de décalage à droite ne consomme pas de ressources logiques, même la porte ET et la porte NON ne sont pas nécessaires, c'est juste la
connexion des lignes .

image-20211107103936593

Le code ci-dessus décale le signal b vers la gauche de deux bits et l'affecte à a. Le circuit matériel correspondant est illustré dans la figure ci-dessous.

image-20211107103951964

(3) L'opérande de l'opération de décalage vers la gauche peut être une constante ou un signal. De même, le numéro de décalage pour une opération de décalage vers la droite peut être soit une constante, soit un signal .

image-20211107104010745

Dans le code ci-dessus, cnt est incrémenté de 1 à chaque horloge, et comme il s'agit de 3 bits, la valeur est 0~2. a est 4'b1000 décalé vers la droite de cnt bits. Lorsque cnt est égal à 0, décaler vers la droite de 0 bit, a est égal à 4'b1000 ; lorsque cnt est égal à 1, décaler vers la droite de 1 bit, a est égal à 4'b0100. Par analogie, le changement de chaque horloge de a est représenté sur la figure ci-dessous.

image-20211107104037650

Semblable à l'opération de décalage à gauche, dans l'opération de décalage à droite, si le numéro de décalage est un signal, le circuit intégré n'est pas une simple connexion, mais peut synthétiser un sélecteur comme illustré dans la figure ci-dessous. Cependant, dans ce cas également, les ressources consommées par de tels circuits matériels sont encore relativement faibles.

image-20211107104055622

5.7.3 Résumé de l'expérience

Multiplier par décalage à gauche

Dans FPGA, l'opération de multiplication doit être évitée autant que possible, car ce type de calcul doit occuper de grandes ressources matérielles et la vitesse de fonctionnement est relativement lente. Lorsque vous devez utiliser la multiplication, essayez de multiplier par 2 à la puissance N , afin que l'opération de multiplication puisse être réalisée en utilisant l'opération de décalage à gauche dans la conception, réduisant ainsi considérablement les ressources matérielles.

Lorsque le multiplicateur est une constante de 2 à la puissance N, la multiplication peut être réalisée par une opération de décalage. Par exemple : a 2 équivaut à a<<1 ; a 4 équivaut à a<<2 ; a*8 équivaut à a<<3, et ainsi de suite. Même si le multiplicateur n'est pas une constante de 2 à la puissance N, la mise en oeuvre peut être simplifiée par une opération de décalage. Par exemple:

image-20211107104134562

b et c dans le code ci-dessus peuvent réaliser a*127, mais la première ligne consomme une multiplication, tandis que la deuxième ligne n'utilise qu'un seul soustracteur
.

image-20211107104147477

Dans le code ci-dessus, b et c peuvent réaliser a*67, mais la première ligne consomme une multiplication, tandis que la deuxième ligne n'utilise que deux additionneurs, économisant ainsi des ressources.

On peut remarquer que les multiplicateurs dans les deux exemples ci-dessus sont tous des constantes, alors ce type de multiplication prend-il également du temps et des efforts pour envisager l'optimisation lors de la conception ? En fait, c'est inutile, car les outils complets sont maintenant très puissants.Lorsque l'outil trouve que le multiplicateur est une constante, il optimisera automatiquement selon le processus ci-dessus.C'est-à-dire que multiplier par une constante ne consomme pas de multiplicateur ressources par essence, vous pouvez donc l'utiliser en toute confiance.

Mais lorsque le multiplicateur n'est pas une constante, il faut faire attention à l'utilisation de la multiplication. Essayez de convertir le signal sous une forme liée à 2 puissance N. Par exemple, lorsque les données doivent être développées et calculées ultérieurement, ne développez pas les données de 100 fois selon la pensée habituelle, mais développez-les directement de 128 fois (Il n'est pas possible d'étendre les décimales par 100 ou 1000 fois selon la pensée conventionnelle, et d'amplifier selon la nième puissance de 2, ce qui réduit l'occupation de l'espace des ressources)。

Utilisez le décalage vers la droite pour mettre en œuvre l'opération de division

Dans la conception de FPGA, il faut éviter au maximum la division, et il est même strictement interdit d'utiliser "/" pour le calcul de la division. En effet, le diviseur occupera une énorme quantité de ressources, qui est supérieure à celle du multiplicateur, et dans de nombreux cas, le résultat ne peut pas être obtenu en un cycle d'horloge. Et lorsque vous devez utiliser la division, vous devez essayer de convertir la division sous la forme d'une division par 2 à la puissance N, afin que vous puissiezUtilisez l'opération de décalage vers la droitePour réaliser l'opération de division, réduisant ainsi considérablement les ressources matérielles .

Lorsque le diviseur est une constante de 2 à la puissance N, la division peut être réalisée par une opération de décalage. Par exemple : a/2 équivaut à a>>1 ; a/4 équivaut à a>>2 ; a/8 équivaut à a>>3, etc.

A la différence du décalage à gauche, lorsque le diviseur n'est pas une constante de 2 à la puissance N, la mise en oeuvre ne peut pas être simplifiée simplement par une opération de décalage. En résumé, la division doit être évitée autant que possible dans la conception de FPGA.

Encodage à chaud utilisant le décalage vers la gauche

Le code one-hot, également appelé code one-hot, est un système de code dans lequel un seul bit vaut 1 et les autres valent tous 0 . Par exemple 8'b00010000, 8'b1000000 etc.

Le code à chaud est très utile dans la conception, il peut être utilisé pour représenter l'état de la machine d'état pour rendre la machine d'état plus robuste, et il peut également être utilisé dans un circuit à choix multiples pour représenter le choix de l'un d'entre eux.

En utilisant l'opération de décalage à gauche, des codes uniques peuvent être facilement générés, par exemple, 4'b0010 peut être généré, qui peut être 4'b1 << 1. De même, un système de code dans lequel un bit est 0 et les autres sont 1 peut également être généré. Par exemple pour générer 4'b1011, cela peut être ~(4'b1 <<2). D'autres résultats numériques souhaités peuvent également être produits en utilisant des décalages vers la gauche :

Par exemple, pour générer 5'b00111, cela pourrait être (5'b1<<3)-1.

Par exemple, pour produire 5'b11100, pourrait être ~((5'b1<<2)-1).

5.8 Opérateurs conditionnels

image-20211107192312809

image-20211107192334644

5.8.1 Opérateur ternaire

**L'opérateur conditionnel (?: )** dans la syntaxe Verilog HDL a trois opérandes (c'est-à-dire un opérateur ternaire) et son format est généralement exprimé comme suit :

image-20211107192358180

Sa signification est : lorsque "l'expression conditionnelle" est vraie (c'est-à-dire logique 1), exécutez la "vraie expression" ; lorsque "l'expression conditionnelle" est fausse (c'est-à-dire logique
0), exécutez la "fausse expression". Autrement dit, lorsque condition_expr est vraie (c'est-à-dire que la valeur est 1), sélectionnez true_expr ; si condition_expr
est fausse (la valeur est 0), sélectionnez false_expr. Si condition_expr est x ou z, le résultat sera la valeur de l'opération au niveau du bit de true_expr et false_expr selon la logique suivante : 0 et 0 donnent 0, 1 et 1 donnent 1, et x sinon.

Les exemples d'application sont les suivants :

image-20211107192422886

Dans l'expression ci-dessus, si s est vrai, affectez t à r ; si s est faux, affectez u à r.

Le schéma de circuit matériel correspondant est illustré ci-dessous.

image-20211107192454340

L'utilisation d'opérateurs conditionnels a les points suivants à noter :

(1) La fonction de l'expression conditionnelle est en fait similaire à un multiplexeur , comme le montre la Figure 1.3-8. En outre, il peut être remplacé par une instruction if-else.

image-20211107192527485

(2) Les opérateurs conditionnels peuvent être utilisés pour l'affectation conditionnelle dans la modélisation de flux de données et, dans ce cas, les expressions conditionnelles agissent comme des commutateurs de contrôle . Par exemple:

image-20211107192550223

Parmi eux, si l'expression Marks > 18 est vraie, alors Grade_A se voit attribuer la valeur d'étudiant ; si Marks > 18 est faux, alors
Grade_C se voit attribuer la valeur d'étudiant.

Le schéma de circuit matériel correspondant est illustré ci-dessous.

image-20211107192607991

(3) Les opérateurs conditionnels peuvent également être imbriqués, et chaque "vraie expression" et "fausse expression" peut elle-même être une
expression . Par exemple:

image-20211107192719206

La signification du code ci-dessus est : si l'expression M == 1 est vraie, alors jugez si CTL est vrai, si CTL est vrai, affectez A à OUT, s'il est faux, affectez B à OUT ; si M = = 1 est faux, alors jugez si CLT est vrai, si CLT est vrai, affectez C à OUT, si faux, affectez D à OUT.

Le schéma de circuit matériel correspondant est le suivant :

image-20211107192740274

5.8.2 instruction if

La syntaxe de l'instruction "if" est la suivante :
if(condition_1)

instruction_procédurale_1;
{sinon si(condition_2)

procedure_statement_2};
{sinon

instruction_procédurale_3} ;

Sa signification est : si la condition_1 est satisfaite, que les autres conditions soient satisfaites ou non, instruction_procédurale_1 sera exécutée, et ni instruction_procédurale_2 ni instruction_procédurale_3 ne seront exécutées.

Si la condition_1 n'est pas satisfaite et que la condition_2 est satisfaite, l'instruction_procédurale_2 est exécutée et ni l'instruction_procédurale_1 ni l'instruction_procédurale_3 ne sont exécutées.

Si la condition_1 n'est pas satisfaite et que la condition_2 n'est pas satisfaite, instruction_procédurale_3 est exécutée et ni instruction_procédurale_1 ni instruction_procédurale_2 ne sont exécutées.

Illustrons par un exemple :

si(Somme < 60) commence

Note = C ;

Total_C = Total _C + 1 ;
fin
sinon si(Somme < 75) début

Note = B ;

Total_B = Total_B + 1 ;
fin
sinon commencer

Note = A ;

Total_A = Total_A + 1 ;
fin

Notez que les expressions conditionnelles doivent toujours être entre parenthèses et peuvent être ambiguës si le format if - if - else est utilisé,
comme illustré dans l'exemple suivant :
if(Clk)
if(Reset)

Q = 0 ;
autre

Q=D ;

Il y a une question ici : à quelle instruction if appartient le dernier else ? Appartient-il à la condition du premier if (Clk) ou à la condition du second if (Reset) ? Cela se fait dans Verilog HDL en combinant le else avec l'instruction if de none Else la plus proche est associée à
résoudre. Dans cet exemple, le else est associé à l'instruction if interne.

Voici un autre exemple d'instruction if :
if(Sum < 100)

Somme = Somme + 10 ;
si(Nickel_In)

Dépôt = 5 ;
Elseif (Dime_In)

Dépôt = 10 ;
sinon si(Quarter_En)

Dépôt = 25 ;
autre

Dépôt = ERREUR ;
suggestion

1. Les expressions conditionnelles doivent être placées entre parenthèses .

2. S'il s'agit d'une instruction if - if, veuillez utiliser l'instruction block begin — end , comme indiqué ci-dessous.
si (Clk) commencer

si (Réinitialiser)

Q = 0 ;

autre

Q=D ;
fin

Les deux suggestions ci-dessus visent à rendre le code plus clair et à éviter les erreurs.

5.8.3 déclaration de cas

L'instruction case est une forme de branche conditionnelle multidirectionnelle et sa syntaxe est la suivante :
case(case_expr)
case_item_expr{case_item_expr} :procedural_statement
. . . . . .
[default:procedural_statement]
endcase

Sous l'instruction case, l'expression conditionnelle case_expr est d'abord évaluée, puis chaque élément de branche est évalué et comparé à son tour, et l'instruction de la première branche qui correspond à la valeur de l'expression conditionnelle est exécutée . Plusieurs éléments de branche peuvent être définis dans 1 branche, et ces valeurs n'ont pas besoin d'être mutuellement exclusives. La branche par défaut remplace toutes les autres branches non couvertes par une expression de branche.

image-20211107193238224

Suggestions d'écriture : L'élément par défaut de l'instruction case doit être écrit pour empêcher la génération de verrous.

5.8.4 Sélectionner l'instruction

Il existe une instruction de sélection couramment utilisée dans la syntaxe Verilog, et sa syntaxe est :

vect[a + : b]或 vect [a - : b]

vect est le nom de la variable, a est la position de départ,Les signes plus ou moins représentent l'ordre croissant ou décroissant, b représente la largeur pour l'ordre croissant ou décroissant .

vect[a +: b] est équivalent à vect[a : a+b-1] , l'intervalle de vect part de a et compte b fois dans la direction supérieure à a ; vect[a -: b] est équivalent à vect [a : a -b+1] , l'intervalle de vect est compté b fois de a à la direction où a est plus petit. a peut être un nombre constant ou variable, mais b doit être une constante.

Exemple 1 : vect[7 + : 3] ; où la position de départ est 7, + représente l'ordre croissant et la largeur est 3. Autrement dit, comptez 3 nombres de 7 à la direction supérieure à 7. Sa forme équivalente est : vect[7 + : 3]== vect[7 : 9].

Exemple 2 : vect[9 - : 4] ; parmi eux, la position de départ est 9, - représente l'ordre décroissant, et la largeur est 4. Autrement dit, comptez 4 nombres de 9 à la direction inférieure à 9. Sa forme équivalente est : vect[9 - : 4]== vect[9 : 6].

La forme la plus courante de cette syntaxe dans l'utilisation réelle consiste à utiliser a comme nombre variable. Par exemple, vous devez concevoir
du code avec les fonctions suivantes :

Lorsque cnt==0, affectez din[7:0] à data[15:8] ; lorsque cnt==1, affectez din[7:0] à data[7:0].

Lors de la conception, il peut être écrit comme : data[15-8 cnt - : 8] <= din[7:0] (À ce stade, 15-8 cnt doit être considéré comme un
tout , ce qui se produira avec le changement de cnt changes), afin que la rationalisation du code soit terminée.

La structure du circuit matériel de l'instruction de sélection est illustrée dans la figure ci-dessous, qui est essentiellement un sélecteur. Lorsque cnt==0, sélectionnez le verrou de data[15:8], affectez din[7:0] à data[15:8], et le verrou de data[7:0] garde la sortie inchangée ; lorsque cnt= =1, sélectionnez le verrou de data[7:0], affectez din[7:0] à data[7:0] et le verrou de data[15:8] conserve la sortie Change.

image-20211107193442661

Conclusion d'expérience : dans les projets réels, la forme de l'énoncé de sélection vect[a + : b] ou vect [a - : b] peut être utilisée pour l'écriture de code, ce qui contribuera à simplifier le code de conception.

5.8.5 Résumé de l'expérience

L'instruction if et l'instruction case sont deux instructions très importantes dans Verilog. Les instructions if et case ont certaines corrélations et
différences. La même chose est que les deux peuvent remplir presque la même fonction.Ce qui suit présente principalement les différences entre les deux.
Il y a une priorité entre chaque branche de l'instruction if, et le circuit synthétisé est similaire à une structure en cascade. Chaque branche de l'instruction case est égale et le circuit synthétisé est un multiplexeur . Par conséquent, le retard du circuit logique obtenu en combinant plusieurs instructions if else-if peut être légèrement supérieur à celui de l'instruction case . Pour les débutants, ils aiment souvent utiliser l'instruction if else-if dans le processus d'apprentissage de Veriolg au début, car cette syntaxe est plus simple à exprimer. Mais dans les projets où la vitesse d'exécution est plus critique, l'effet de l'utilisation de l'instruction case sera meilleur. Comparons-le à travers un cas spécifique, en utilisant l'instruction if et l'instruction case pour décrire le résultat complet du même circuit fonctionnel.

Le premier est le code écrit avec l'instruction if :

image-20211107193601401

image-20211107193618227

Sa vue RTL synthétisée est illustrée ci-dessous.

image-20211107193643645

Comme on peut le voir sur le diagramme RTL illustré dans la figure ci-dessus, ce circuit contient deux multiplexeurs deux à un, et la priorité du côté droit est supérieure à celle du côté gauche (car la valeur de q est directement liée à la sélection deux pour un sur la bonne connexion de l'appareil), lorsque en[0] n'est pas 1, il continuera à évaluer en[1]. C'est dedansLe circuit synthétisé sous l'instruction if a la priorité

Ensuite, analysez le code décrit à l'aide de l'instruction case.

image-20211107193718179

Sa vue RTL synthétisée est la suivante :

image-20211107193742651

Comme on peut le voir,Le circuit synthétisé par le code logique écrit par l'instruction case est parallèle, n'a pas de priorité et n'affecte pas la vitesse de fonctionnement du circuit

Bien que dans la vue RTL, il y ait une grande différence entre les circuits synthétisés par les deux déclarations, mais comme les outils de développement actuels sont suffisamment intelligents, le circuit sera automatiquement optimisé lors de la mise en page et du routage, et il sera éventuellement mappé sur le circuit à l'intérieur du FPGA. Il n'y a fondamentalement aucune différence entre eux .

image-20211107193823156

image-20211107193833956

Enfin, résumez la différence et le lien entre l'instruction if et l'instruction case :

L'instruction If a la priorité et la partie après le else est exécutée uniquement lorsque la condition sous if n'est pas remplie. L'instruction case est parallèle et n'a pas de priorité, ce qui peut être clairement observé dans la vue RTL synthétisée par les deux. Cependant, étant donné que les outils de simulation et de synthèse actuels sont suffisamment puissants, les résultats de synthèse finaux des instructions if...else... et case... ne sont en fait pas différents, ce ne sont que deux méthodes d'implémentation différentes, il n'y a donc fondamentalement pas besoin de considérer la différence entre les deux différences. En partant du principe de ne pas affecter la fonction, le concepteur n'a pas besoin d'effectuer un travail d'optimisation local, par exemple, il n'est pas nécessaire de prendre en compte la différence de consommation de ressources des instructions if/case, et il n'est pas nécessaire d'envisager l'optimisation des circuits. Uniquement sous le principe d'affecter la fonction (c'est-à-dire de signaler une erreur due à des contraintes de séquence), optimisez le circuit en fonction des invites.

5.9 Opérateurs de concaténation

image-20211107193940242

image-20211107193949282

L'opération de concaténation est une opération qui combine de petites expressions pour former une grande expression, et sa forme est la suivante :

{expr1, expr2, . . ., exprN} ;

Le caractère de splicing ne consomme aucune ressource matérielle, il change juste la combinaison de lignes , vous pouvez vous référer aux exemples suivants :

image-20211107194014357

La concaténation de constantes de longueur non fixe n'est pas autorisée car la longueur des constantes de longueur non fixe est inconnue. Par conséquent, le code ci-dessous n'est pas grammatical. {Dbus,5} ; //les opérations de concaténation sur des constantes de longueur non fixe ne sont pas autorisées

Je suppose que tu aimes

Origine blog.csdn.net/Royalic/article/details/121196365
conseillé
Classement