Accélérer un programme pour un processeur synthétisé Redd sans optimisation: remplacer une horloge

Jusqu'à présent, nous avons discuté de la façon d'augmenter la vitesse du système en utilisant des méthodes intensives. Mais en fait, il existe de nombreuses méthodes. Nous travaillons maintenant à une fréquence d'horloge de 50 MHz, qui est associée à l'utilisation d'un composant de l'ensemble pour un programme universitaire (et sans cela, il est impossible de synchroniser la SDRAM, ce qui nécessite que les impulsions d'horloge allant vers le microcircuit soient décalées par rapport aux principales). Lorsque j'ai introduit ce composant dans le jeu, j'ai averti que cette solution était temporaire. Puis j'ai jeté tellement de nouvelles informations sur le lecteur que toute fatigue supplémentaire pourrait conduire à une exclamation: "Eh bien, ces FPGA, tout est si compliqué ici!" Maintenant, nous construisons déjà facilement et naturellement des systèmes de processeur, toutes les choses terribles sont derrière nous. Il est temps de comprendre comment vous pouvez créer votre propre composant, ce qui vous permet d'augmenter la fréquence d'horloge du processeur et des périphériques qui y sont connectés.



Articles précédents de la série:


  1. Développement du «firmware» le plus simple pour les FPGA installés dans Redd, et débogage en utilisant le test de mémoire comme exemple.
  2. Développement du «firmware» le plus simple pour les FPGA installés dans Redd. Partie 2. Code de programme.
  3. Développement de son propre noyau pour l'intégration dans un système de processeur FPGA.
  4. Développement de programmes pour le processeur central Redd sur l'exemple d'accès au FPGA.
  5. Les premières expériences utilisant le protocole de streaming sur l'exemple de la connexion du CPU et du processeur dans le FPGA du complexe Redd.
  6. Joyeux Quartusel, ou comment le processeur est arrivé à une telle vie.
  7. Méthodes d'optimisation de code pour Redd. Partie 1: effet de cache.
  8. Méthodes d'optimisation de code pour Redd. Partie 2: mémoire non mise en cache et fonctionnement sur bus parallèle.

Un peu de raisonnement théorique


Estimons la fréquence que nous pouvons régler sans douleur pour le cadencement de tout notre fer. La puce SDRAM utilisée dans le complexe permet une fréquence limite de 133 MHz. Pour les vitesses d'horloge du processeur, consultez les Benchmarks de performance Nios II . Là, pour notre FPGA Cyclone IV E, la fréquence centrale Nios II / f de 160 MHz est garantie. Je ne suis pas partisan de retirer tous les jus du système, nous allons donc parler de travailler à une fréquence de 100 MHz.

Pour être honnête, je n'ai toujours pas été inspiré par la méthodologie de calcul du décalage de fréquence d'horloge donnée dans la section 32.7. Considérations d'horloge, de PLL et de synchronisation du Guide de l'utilisateur des périphériques intégrés IP , mais il semble que je ne suis pas le seul. Au moins, une longue recherche sur le net ne m'a pas conduit à des articles qui contiendraient des résultats calculés de la même manière, mais pas pour la fréquence qui est donnée dans le document principal (ces mêmes 50 MHz).

Il y a un article intéressant auquel je donnerai un lien direct www.emb4fun.de/fpga/nutos1/index.html . On pourrait simplement s'y référer et dire "Faisons comme l'auteur", sinon pour un "mais": l'auteur de cet article utilise le bloc PLL (en russe - PLL, et au niveau des ménages - un convertisseur de fréquence), insérant son propre code en VHDL. Comme je l'ai déjà noté dans l' article sur le fun Quartusel , j'adhère à l'idéologie selon laquelle le système de processeur devrait être au plus haut niveau de la hiérarchie du projet. Aucune insertion n'est nécessaire dans aucune langue, que ce soit VHDL ou Verilog. Récemment, cette approche a reçu une confirmation de plus: nous avons un nouvel employé, un étudiant qui ne parle pas encore Verilog, mais qui fait du code pour le complexe Redd parfaitement, puisque l'approche choisie le permet.

Il s'avère que nous prenons simplement pour base que tout fonctionne pour l'auteur avec un décalage de moins 54 degrés (quel type de degrés est décrit dans l'article, le lien auquel j'ai donné le paragraphe ci-dessus).

Ensuite, faites attention à un autre article intéressant asral.unimap.edu.my/wp-content/uploads/2019/07/2019_IJEECS_Chan_Implementation-Camera-System.pdf . Tout fonctionne pour les auteurs à un décalage de moins 65 degrés.

Essayons de rendre notre système en utilisant une valeur de cette plage. Si lors du test quotidien de RAM il n'y a pas un seul dysfonctionnement, alors nous laisserons cette valeur comme combat. Nous avons le droit, car le «firmware» développé pour Redd ne sera pas remis aux clients, mais sera utilisé pour les besoins internes et en quantités unitaires. Si quoi que ce soit, il sera toujours possible de tout réparer sans difficultés inutiles (des difficultés surviennent lorsqu'il est nécessaire de mettre à jour le «firmware» dans des milliers d'appareils vendus, et simplement chez le Client distant).

Nouvelle configuration matérielle


Pour une raison quelconque, il me semble que le système de processeur de cet article est plus facile à faire à partir de zéro que à refaire à partir de l'ancien. Juste comment démontrer le processus «tordre, tordre, vouloir confondre», se référant constamment aux articles précédents, je préfère tout montrer à nouveau dès le début. En même temps, nous réparons le matériel. Commençons donc.

Au tout début, on nous montre un système complètement vide contenant uniquement une horloge et une source de signal de réinitialisation.



Habituellement, je n'y change rien, mais aujourd'hui je vais faire une exception. Je ne veux pas être distrait par le circuit de réinitialisation, car nous continuerons à travailler sous le débogueur. Par conséquent, je changerai la condition de réinitialisation du niveau à une différence négative, et la jambe elle-même sera ensuite annulée.



Mais ici, le signal d'horloge a une fréquence de 50 MHz (cette fréquence est fixée par les caractéristiques du générateur soudé à la carte). Dans le premier des articles que j'ai mentionnés ci-dessus, le bloc PLL ajouté au projet principal a été utilisé. Où l'obtenons-nous ici? Et le voilà!



Il s'agit du même bloc, mais ici nous n'avons pas besoin d'incorporer de code dans Verilog ou VHDL. Tout est déjà inséré pour nous! Certes, le réglage pour différents types de FPGA diffère légèrement plus que complètement. Plus précisément, les paramètres ajustables sont plus ou moins les mêmes, mais ils sont situés à des endroits fondamentalement différents dans les boîtes de dialogue de configuration. Étant donné que le Cyclone IV E FPGA est utilisé dans le complexe Redd, nous considérerons la configuration de cette option.

Dans le premier onglet, remplacez la fréquence d'entrée par 50 MHz (par défaut, elle était de 100) et passez à l'onglet suivant (cliquez sur Suivant, pour Cyclone IV E, nous devons le faire plusieurs fois).



Décochez les entrées et sorties supplémentaires. Nous n'en avons pas besoin:



Nous sautons les prochains onglets jusqu'à ce que nous arrivions à définir la sortie C0. Là, nous commutons le bouton radio pour régler la fréquence et entrer la valeur de 100 MHz:



Avec C1, les choses sont un peu plus compliquées. Tout d'abord, cochez la case indiquant qu'elle doit également être utilisée. Deuxièmement, nous avons également réglé la fréquence de 100 MHz. Eh bien, et troisièmement, nous avons réglé le décalage de fréquence. Lequel demander? Moins 58 ou moins 65? Bien sûr, j'ai essayé les deux options. Les deux m'ont mérité. Mais l'argument sur le sujet moins 58 semble un peu moins convaincant, donc ici je recommanderai d'entrer la valeur moins 65 degrés (alors que l'automatisation me dira que la valeur réelle atteinte sera de moins 63 degrés).



Eh bien, c'est tout. Vous pouvez maintenant parcourir le bouton Suivant jusqu'à la fin, ou vous pouvez simplement cliquer sur Terminer . Nous connectons les entrées inclk_interface et inclk_interface_reset . La sortie c0 sera utilisée comme horloge pour l'ensemble du système. La sortie c1 est exportée pour cadencer la puce sdram . À l'avenir, vous devrez vous rappeler de connecter le bus de données à l'entrée pll_slave . Pour le cyclone V, cela ne serait pas nécessaire.



Autres pièces de quincaillerie, purement pour fixer le matériel


Ajoutez un cœur de processeur. Aujourd'hui, notre SDRAM sera soumise à des tests. Ainsi, le code ne doit pas s'y trouver. Et cela, à son tour, signifie que tout le code sera situé dans la RAM interne du FPGA. Autrement dit, nous n'avons pas besoin d'un cache d'instructions. Éteignez-le, économisez de la mémoire FPGA. Nous connectons également un bus d'instructions et de données hautement connecté. Aucun réglage plus intéressant pour le cœur du processeur n'est requis.



Avec le mouvement habituel de la main, ajoutez deux blocs de RAM FPGA interne. L'un est un port double d'une capacité de 16 kilo-octets et l'autre est un port unique d'une capacité de 4 kilo-octets. Comment les nommer et comment se connecter, j'espère que tout le monde s'en souvient. La dernière fois que j'ai aimé mettre en valeur les pneus à fleurs, peut-être pour en faciliter la lecture, je le ferai dans cet article.



N'oubliez pas d'attribuer à ces blocs de mémoire des adresses spéciales dans la plage personnelle et de les verrouiller. Laissez CodeMemory être affecté à 0x20000000 et DataMemory à 0x20004000.

Eh bien, ajoutons un bloc SDRAM au système, le configurons, ainsi que des blocs JTAG-UART pour afficher les messages et un GPIO à un bit, sur lequel nous mesurerons la fréquence réelle pour nous assurer qu'elle augmente. Pour référence, voici quelques paramètres non évidents:







Au total, nous obtenons un tel système (j'ai mis en évidence le bus de données, car il scanne tous les appareils externes):



Nous attribuons des vecteurs au processeur, attribuons automatiquement des adresses, attribuons automatiquement des numéros d'interruption, au système de génération.

Nous connectons le système au projet, faisons un assemblage grossier, attribuons les numéros de jambe, et cette fois, nous rendons virtuel non seulement CKE , mais aussi reset_n (comment cela se fait, je l'ai dit dans l' un des articles précédents , recherchez Virtual Pin là-bas). Nous faisons l'assemblage final, remplissons l'équipement dans le FPGA. C’est tout. Nous avons terminé l'équipement, allez à la partie logiciel.

Nous mettons en place BSP pour notre environnement


Pour changer, créons un projet basé sur un modèle non pas de Hello World Small , mais de Memory Test Small :



Une fois créé, allez dans l'éditeur BSP. Comme d'habitude, la première chose que nous faisons est de désactiver la vérification SysID et d'autoriser l'utilisation de C ++ (bien que cette fois je ne changerai pas le type de fichier, mais c'est déjà une habitude pour moi):



Mais la chose la plus importante que nous devons corriger dans l'onglet Script de l' éditeur de liens . L'automatisation a reconnu que le bus d'instructions ne va qu'à la mémoire CodeMemory et a donc placé une section de code (appelée .text ) dans la mémoire CodeMemory . Mais en prenant soin de nous, elle a placé tout le reste dans la plus grande région de données, qui est située dans SDRAM . Comment savait-elle que nous effacerions sans pitié ce souvenir?



Nous devrons manuellement, ligne par ligne, remplacer la région par DataMemory (les listes de sélection y apparaîtront, la sélection devra y être réorganisée). Nous devrions obtenir cette image:



Expériences du programme


Nous quittons l'éditeur, générons le BSP, essayons d'exécuter le programme de débogage. Nous obtenons le texte suivant:



Si j'appuie sur Entrée, je n'ai pas réussi. J'ai entré quelque chose (oui même un espace), puis j'ai appuyé sur Entrée. Puis ils m'ont demandé:



Heure par heure n'est pas plus facile. Et quelle adresse entrer? Vous pouvez ouvrir Platform Designer et y voir la valeur. Mais je regarde généralement dans le fichier de référence universel system.h (le chemin complet de mon projet est C: \ Work \ CachePlay5 \ software \ MemoryTest_bsp \ system.h). Là, nous sommes intéressés par deux lignes:



Même texte
#define ALT_MODULE_CLASS_new_sdram_controller_0 altera_avalon_new_sdram_controller #define NEW_SDRAM_CONTROLLER_0_BASE 0x0 #define NEW_SDRAM_CONTROLLER_0_CAS_LATENCY 3 #define NEW_SDRAM_CONTROLLER_0_CONTENTS_INFO #define NEW_SDRAM_CONTROLLER_0_INIT_NOP_DELAY 0.0 #define NEW_SDRAM_CONTROLLER_0_INIT_REFRESH_COMMANDS 2 #define NEW_SDRAM_CONTROLLER_0_IRQ -1 #define NEW_SDRAM_CONTROLLER_0_IRQ_INTERRUPT_CONTROLLER_ID -1 #define NEW_SDRAM_CONTROLLER_0_IS_INITIALIZED 1 #define NEW_SDRAM_CONTROLLER_0_NAME "/dev/new_sdram_controller_0" #define NEW_SDRAM_CONTROLLER_0_POWERUP_DELAY 100.0 #define NEW_SDRAM_CONTROLLER_0_REFRESH_PERIOD 15.625 #define NEW_SDRAM_CONTROLLER_0_REGISTER_DATA_IN 1 #define NEW_SDRAM_CONTROLLER_0_SDRAM_ADDR_WIDTH 0x18 #define NEW_SDRAM_CONTROLLER_0_SDRAM_BANK_WIDTH 2 #define NEW_SDRAM_CONTROLLER_0_SDRAM_COL_WIDTH 9 #define NEW_SDRAM_CONTROLLER_0_SDRAM_DATA_WIDTH 16 #define NEW_SDRAM_CONTROLLER_0_SDRAM_NUM_BANKS 4 #define NEW_SDRAM_CONTROLLER_0_SDRAM_NUM_CHIPSELECTS 1 #define NEW_SDRAM_CONTROLLER_0_SDRAM_ROW_WIDTH 13 #define NEW_SDRAM_CONTROLLER_0_SHARED_DATA 0 #define NEW_SDRAM_CONTROLLER_0_SIM_MODEL_BASE 0 #define NEW_SDRAM_CONTROLLER_0_SPAN 33554432 #define NEW_SDRAM_CONTROLLER_0_STARVATION_INDICATOR 0 


où décimal 33554432 est égal à hex 0x2000000. Par conséquent, mes réponses et le résultat du travail devraient ressembler à ceci:



Génial, mais ce n'est pas bon pour un test quotidien. J'ai réécrit la fonction principale comme ceci:

 int main(void) { int step = 0; while (1) { if (step++%100 == 0) { alt_printf ("."); } if (MemTestDevice(NEW_SDRAM_CONTROLLER_0_BASE, NEW_SDRAM_CONTROLLER_0_SPAN)!=0) { printf ("*"); } } return (0); } 

Les points indiquent que le programme n'a pas «gelé». En cas d'erreur, un astérisque s'affiche. Pour plus de fiabilité, vous pouvez mettre un point d'arrêt sur sa sortie, puis ne pas le mettre en veille.

Certes, les points «gauche» ont grimpé de quelque part. Il s'est avéré qu'ils sont affichés à l'intérieur de la fonction MemTestDevice () . Là, j'ai effacé leur conclusion. Le test a réussi. Le système résultant peut être utilisé, au moins pour des besoins internes (à savoir, de tels développements sont effectués dans le cadre du complexe Redd).

Vérification des performances du système


Mais je suis déjà habitué au fait que lorsque vous travaillez avec un équipement, vous ne pouvez faire confiance à rien. Tout doit être vérifié soigneusement. Faisons en sorte de travailler à une fréquence doublée par rapport aux articles précédents. Ajoutez la fonction bien connue MagicFunction1 ().

Permettez-moi de vous rappeler à quoi elle ressemble.
 void MagicFunction1() { IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); } 


Nous l'appellerons de main () , nous capterons les impulsions sur l'oscilloscope, mais cette fois nous ferons attention non seulement à leur beauté, mais aussi à la fréquence (permettez-moi de vous rappeler que chaque goutte, même de haut en bas, est une commande, afin que vous puissiez mesurer la distance entre les gouttes )



Seulement 50 mégahertz. La fréquence n'a-t-elle vraiment pas augmenté? Comparez avec la fréquence du code développé lors de la rédaction du dernier article, et nous comprenons que tout est en ordre. C'est juste que l'unité pio ordinaire nécessite 2 cycles d'horloge par broche sur le port (dans une unité maison, j'ai 1 horloge, mais ici, il nous suffit de nous assurer que les performances du système ont doublé).



Conclusion


Au lieu d'utiliser un oscillateur à fréquence fixe, nous avons appris à utiliser une unité PLL personnalisée. Certes, les constantes détectées sont destinées à une fréquence de 100 MHz, mais tout le monde peut les ajuster à n'importe quelle autre fréquence soit à l'aide de calculs bien connus, soit par essais et erreurs. Nous avons également renforcé les compétences de création d'un système de processeur optimal et veillé à ce que la mémoire à une fréquence plus élevée fonctionne de manière stable et que la fréquence augmente vraiment.

En général, nous pouvons déjà produire des choses informatiques, nous pouvons même échanger avec le processeur central, mais le processeur central du complexe fera face aux calculs banaux plus efficacement. FPGA est ajouté à Redd afin de mettre en œuvre des interfaces à haute vitesse ou de capturer (enfin ou jouer) des flux d'informations. Nous avons déjà maîtrisé les bases de la conception, nous avons appris à fournir des performances plus ou moins élevées. Il est temps de continuer à travailler avec les interfaces, c'est ce que nous ferons dans le prochain article. Plus précisément, un ensemble d'articles, respectant la règle «un article - une chose».

Source: https://habr.com/ru/post/fr469985/


All Articles