Dans le fil de commentaires sur une réponse à cette question: Sorties incorrectes dans l'entité VHDL, il a été déclaré:
"Avec des entiers, vous n'avez pas le contrôle ou l'accès à la représentation logique interne dans le FPGA, tandis que SLV vous permet de faire des trucs comme l'utilisation efficace de la chaîne de transport"
Alors, dans quelles circonstances avez-vous trouvé plus pratique de coder en utilisant un vecteur de représentation de bits qu'en utilisant des entiers pour accéder à la représentation interne? Et quels avantages avez-vous mesurés (en termes de surface de puce, de fréquence d'horloge, de retard ou autre)?
Réponses:
J'ai écrit le code suggéré par deux autres affiches à la fois
vector
etinteger
sous forme, en veillant à ce que les deux versions fonctionnent de la manière la plus similaire possible.J'ai comparé les résultats en simulation, puis synthétisé à l'aide de Synplify Pro ciblant Xilinx Spartan 6. Les exemples de code ci-dessous sont collés à partir du code de travail, vous devriez donc pouvoir les utiliser avec votre synthétiseur préféré et voir s'il se comporte de la même manière.
Décomptes
Tout d'abord, le downcounter, comme l'a suggéré David Kessner:
Architecture vectorielle:
Architecture entière
Résultats
Côté code, l'entier me semble préférable car il évite les
to_unsigned()
appels. Sinon, pas grand chose à choisir.Son exécution via Synplify Pro avec
top := 16#7fff_fffe#
produit 66 LUT pour lavector
version et 64 LUT pour lainteger
version. Les deux versions utilisent beaucoup la chaîne de transport. Les deux signalent des vitesses d'horloge supérieures à 280 MHz . Le synthétiseur est tout à fait capable d'établir une bonne utilisation de la chaîne de transport - J'ai vérifié visuellement avec le visualiseur RTL qu'une logique similaire est produite avec les deux. De toute évidence, un compteur avec comparateur sera plus grand, mais ce serait la même chose avec les entiers et les vecteurs.Division par 2 ** n compteurs
Suggéré par ajs410:
Architecture vectorielle
Architecture entière
Vous devez sauter à travers certains cerceaux pour éviter de simplement utiliser
to_unsigned
puis de retirer des bits qui produiraient clairement le même effet que ci-dessus:Résultats
Côté code, dans ce cas, la
vector
version est nettement meilleure!En termes de résultats de synthèse, pour ce petit exemple, la version entière (comme l'a annoncé ajs410) produit 3 LUT supplémentaires dans le cadre des comparateurs, j'étais trop optimiste quant au synthétiseur, bien qu'il fonctionne avec un morceau de code terriblement obscurci!
Autres utilisations
Les vecteurs sont clairement gagnants lorsque vous voulez que l'arithmétique se termine (les compteurs peuvent être effectués sur une seule ligne):
contre
bien qu'au moins il ressorte clairement de ce code que l'auteur avait l'intention de boucler.
Quelque chose que je n'ai pas utilisé en code réel, mais que j'ai réfléchi:
La fonction «naturellement enveloppante» peut également être utilisée pour «calculer par débordements». Lorsque vous savez que la sortie d'une chaîne d'additions / soustractions et de multiplications est limitée, vous n'avez pas à stocker les bits élevés des calculs intermédiaires car (en complément de 2 s), ils sortiront "au lavage" au moment où vous arrivez à la sortie. On me dit que ce document en contient une preuve, mais il m'a paru un peu dense pour faire une évaluation rapide! Théorie de l'addition et des débordements informatiques - HL Garner
L'utilisation de
integer
s dans cette situation entraînerait des erreurs de simulation lors de leur encapsulation, même si nous savons qu'ils se dérouleront à la fin.Et comme Philippe l'a souligné, lorsque vous avez besoin d'un nombre supérieur à 2 ** 31, vous n'avez pas d'autre choix que d'utiliser des vecteurs.
la source
variable c : unsigned(32 downto 0);
... n'est-ce pasc
une variable de 33 bits?Lors de l'écriture de VHDL, je recommande fortement d'utiliser std_logic_vector (slv) au lieu de integer (int) pour SIGNALS . (D'un autre côté, utiliser int pour les génériques, certaines constantes et certaines variables peuvent être très utiles.) Autrement dit, si vous déclarez un signal de type int, ou devez spécifier une plage pour un entier, vous faites probablement Quelque chose ne va pas.
Le problème avec int est que le programmeur VHDL n'a aucune idée de la représentation logique interne de l'int, et nous ne pouvons donc pas en profiter. Par exemple, si je définis un entier compris entre 1 et 10, je n'ai aucune idée de la façon dont le compilateur code ces valeurs. Espérons que ce serait codé en 4 bits, mais nous ne savons pas grand-chose au-delà. Si vous pouviez sonder les signaux à l'intérieur du FPGA, il pourrait être codé de "0001" à "1010", ou codé de "0000" à "1001". Il est également possible qu'il soit codé d'une manière qui n'a absolument aucun sens pour nous, les humains.
Au lieu de cela, nous devrions simplement utiliser slv au lieu de int, car nous contrôlons alors le codage et avons également un accès direct aux bits individuels. Avoir un accès direct est important, comme vous le verrez plus tard.
Nous pourrions simplement lancer un int en slv chaque fois que nous avons besoin d'accéder aux bits individuels, mais cela devient vraiment désordonné, très rapide. C'est comme obtenir le pire des deux mondes au lieu du meilleur des deux mondes. Votre code sera difficile à optimiser pour le compilateur et presque impossible à lire. Je ne le recommande pas.
Donc, comme je l'ai dit, avec slv, vous avez le contrôle sur les codages de bits et l'accès direct aux bits. Alors, que pouvez-vous faire avec ça? Je vais vous montrer quelques exemples. Disons que vous devez émettre une impulsion toutes les 4 294 000 000 d'horloges. Voici comment procéder avec int:
Et le même code en utilisant slv:
La plupart de ce code est identique entre int et slv, au moins dans le sens de la taille et de la vitesse de la logique résultante. Bien sûr, l'un compte et l'autre compte à rebours, mais ce n'est pas important pour cet exemple.
La différence réside dans "la ligne importante".
Avec l'exemple int, cela va aboutir à un comparateur à 32 entrées. Avec les LUT à 4 entrées utilisées par le Xilinx Spartan-3, cela nécessitera 11 LUT et 3 niveaux de logique. Certains compilateurs peuvent convertir cela en une soustraction qui utilisera la chaîne de transport et s'étendra sur l'équivalent de 32 LUT mais pourrait fonctionner plus rapidement que 3 niveaux de logique.
Avec l'exemple slv, il n'y a pas de comparaison 32 bits, c'est donc "zéro LUT, zéro niveau de logique". La seule pénalité est que notre compteur est un bit supplémentaire. Étant donné que la synchronisation supplémentaire pour ce bit supplémentaire de compteur est entièrement dans la chaîne de transport, il y a un retard de synchronisation supplémentaire "presque nul".
Bien sûr, c'est un exemple extrême, car la plupart des gens n'utiliseraient pas un compteur 32 bits de cette manière. Cela s'applique aux compteurs plus petits, mais la différence sera moins dramatique, mais toujours significative.
Ce n'est qu'un exemple de la façon d'utiliser slv over int pour obtenir un timing plus rapide. Il existe de nombreuses autres façons d'utiliser slv - cela ne prend que de l'imagination.
Mise à jour: ajout de trucs pour répondre aux commentaires de Martin Thompson sur l'utilisation de int avec "if (count-1) <0"
(Remarque: je suppose que vous vouliez dire "si count <0", car cela le rendrait plus équivalent à ma version slv et éliminerait le besoin de cette soustraction supplémentaire.)
Dans certaines circonstances, cela peut générer l'implémentation logique prévue, mais il n'est pas garanti de fonctionner tout le temps. Cela dépendra de votre code et de la façon dont votre compilateur code la valeur int.
En fonction de votre compilateur et de la façon dont vous spécifiez la plage de votre int, il est tout à fait possible qu'une valeur int de zéro ne soit pas codée en un vecteur binaire "0000 ... 0000" lorsqu'elle est intégrée dans la logique FPGA. Pour que votre variation fonctionne, elle doit coder en "0000 ... 0000".
Par exemple, disons que vous définissez un int pour avoir une plage de -5 à +5. Vous vous attendez à ce qu'une valeur de 0 soit codée en 4 bits comme "0000", et +5 en "0101" et -5 en "1011". Il s'agit du schéma de codage typique à deux compléments.
Mais ne supposez pas que le compilateur va utiliser deux compléments. Bien que inhabituel, le complément à un pourrait entraîner une "meilleure" logique. Ou bien, le compilateur pourrait utiliser une sorte de codage "biaisé" où -5 est codé comme "0000", 0 comme "0101" et +5 comme "1010".
Si le codage de l'int est "correct", le compilateur déduira probablement quoi faire avec le bit de retenue. Mais si elle est incorrecte, la logique résultante sera horrible.
Il est possible que l'utilisation d'un int de cette manière puisse entraîner une taille et une vitesse logiques raisonnables, mais ce n'est pas une garantie. Le passage à un autre compilateur (XST vers Synopsis par exemple) ou le passage à une architecture FPGA différente pourrait provoquer la mauvaise chose exacte.
Non signé / signé vs slv est encore un autre débat. Vous pouvez remercier le comité du gouvernement américain de nous avoir donné autant d'options en VHDL. :) J'utilise slv car c'est la norme d'interface entre les modules et les cœurs. En dehors de cela, et de certains autres cas dans les simulations, je ne pense pas qu'il y ait un énorme avantage à utiliser slv sur signé / non signé. Je ne suis pas sûr non plus que les signaux à trois états pris en charge signés / non signés.
la source
if (count-1) < 0
je pense que le synthétiseur déduira le bit de fin et produira à peu près le même circuit que votre exemple slv. De plus, ne devrions-nous pas utiliser leunsigned
type de nos jours :)Mon conseil est d'essayer les deux, puis d'examiner la synthèse, la carte et les rapports de lieu et itinéraire. Ces rapports vous indiqueront exactement combien de LUT consomment chaque approche, ils vous indiqueront également la vitesse maximale à laquelle la logique peut fonctionner.
Je suis d'accord avec David Kessner que vous êtes à la merci de votre chaîne d'outils et qu'il n'y a pas de "bonne" réponse. La synthèse est de la magie noire et la meilleure façon de savoir ce qui s'est passé est de lire attentivement et attentivement les rapports produits. Les outils Xilinx vous permettent même de voir à l'intérieur du FPGA, jusqu'à la façon dont chaque LUT est programmée, comment la chaîne de transport est connectée, comment la structure du commutateur connecte toutes les LUT, etc.
Pour un autre exemple dramatique de l'approche de M. Kessner, imaginez que vous voulez avoir plusieurs fréquences d'horloge à 1/2, 1/4, 1/8, 1/16, etc. Vous pouvez utiliser un entier qui compte constamment à chaque cycle, puis avoir plusieurs comparateurs par rapport à cette valeur entière, chaque sortie de comparateur formant une division d'horloge différente. Selon le nombre de comparateurs, le fanout peut devenir déraisonnablement important et commencer à consommer des LUT supplémentaires juste pour la mise en mémoire tampon. L'approche SLV prendrait simplement chaque bit individuel du vecteur comme sortie.
la source
Une raison évidente est que les signés et les non signés autorisent des valeurs plus grandes que l'entier 32 bits. C'est une faille dans la conception du langage VHDL, qui n'est pas essentielle. Une nouvelle version de VHDL pourrait résoudre ce problème, nécessitant des valeurs entières pour prendre en charge la taille arbitraire (semblable à BigInt de Java).
En dehors de cela, je suis très intéressé d'entendre parler de benchmarks qui fonctionnent différemment pour les entiers par rapport aux vecteurs.
BTW, Jan Decaluwe a écrit un bel essai à ce sujet: Ces Ints sont faits pour Countin '
la source