Concevoir des composants numériques en Verilog passe nécessairement par la simulation. Pour simuler du Verilog, il existe un logiciel open source nommé Icarus qui remplit bien sa fonction. Il existe également des simulateurs non libres qui sont généralement plus performants. Mais tous ces simulateurs ont le même défaut, ils sont lents. Verilator est un simulateur un peu particulier qui se concentre sur la partie synthétisable du Verilog et génère un objet C++ que l’on va simuler au moyen d’un programme écrit dans ce même langage. Cette approche permet un gain de l’ordre d’une trentaine de fois plus rapide qu’Icarus dans l’exemple que nous allons voir. Il est également nettement plus rapide que tous les simulateurs commerciaux.
La simulation est nécessaire en Verilog. En effet, déverminer un gateware « en réel » nécessite du matériel onéreux et des montages électroniques compliqués. Avec la simulation, il est possible de visualiser n’importe quel signal interne sans avoir à brancher de sonde ou à souder des connecteurs sur le produit fini.
Traditionnellement, on génère les stimulus des signaux d’entrée en les décrivant en Verilog. En effet, le langage inclut toutes les fonctions nécessaires à l’écriture des bancs de test en simulation. Il est donc possible de se contenter du Verilog pour tester l’intégralité de son composant.
Le problème de la simulation, c’est qu’elle mobilise beaucoup de ressources tant en termes de calcul processeur que de mémoire vive ou même de mémoire de masse pour enregistrer les traces. Et surtout, cette simulation prend du temps.
L’approche proposée par Verilator [1] est de transformer la sous-partie « synthétisable » du Verilog en un objet C++ que l’on instancie ensuite dans un banc de test écrit en C++. Bien sûr, cela suppose que les développeurs et développeuses aient des compétences en C++, en plus de leurs connaissances du Verilog. Il est également possible d’utiliser la librairie SystemC du consortium Accelera, spécialisée dans la description matérielle (HDL).
Afin de bien saisir les tenants et les aboutissants de ces deux méthodes, nous allons réaliser un projet Verilog qui affiche une image sur un écran VGA. Nous le simulerons d’abord avec Icarus et un banc de test en Verilog, puis nous passerons à la simulation Verilator avec l’écriture d’un banc de test en C++.
1. Le projet vidéo
Le module vidéo est une reprise du projet SimpleVga présenté dans l’article de présentation de la carte Colorlight [2].
Il consiste à afficher les bandes de couleurs différentes entourées d’un rectangle blanc. Le tout avec une résolution de type VGA « classique » de 640x480.
Le module est composé de deux fichiers Verilog : le « top », nommé RgbVideo donné ci-dessous se charge de calculer les couleurs des pixels à envoyer en sortie.
Chaque couleur (0) est codée sur 1 bit, quand il est à 1 on envoie 0,7 v sur le signal VGA pour afficher la couleur, et quand il est à 0 on laisse la sortie à 0 volt.
Le niveau de tension est forcé à 0,7 v au moyen de résistances en série comme présenté sur la figure 2.
Nous avons ainsi 2 couleurs possibles par signal pour afficher les différentes couleurs à l’écran.
Le module HVSync (1) se charge quant à lui de générer les différents signaux de synchronisation vidéo hsync_o, vsync_o et display_on. Ce module génère également les compteurs de position horizontale hpos et verticale vpos.
Cette partie génération des signaux de synchronisation est complexe, car il faut configurer les différents temps ainsi que les « pauses » avant et après l’affichage d’une ligne.
Le motif affiché à l’écran est composé de raies de toutes les couleurs affichables (3) entourées d’un rectangle blanc (2). Le rendu final est visible avec l’image 3.
Tout le code présenté ici est bien sûr disponible sur le projet GitHub [3] de l’auteur avec tous les scripts de configuration et Makefiles nécessaire à la reproduction des manipulations décrites ici.
Dans le cadre de cet article, nous nous contenterons de simuler (faire tourner) le module pour enregistrer une image vidéo de sortie au format PPM [4]. Nous inclurons également les zones non affichables de synchronisation dans l’image pour les visualiser (en noir).
L’avantage de ce projet est qu’il est facile à simuler, puisque les seuls signaux d’entrée nécessaires sont l’horloge clk_i et le reset rst_i, l’image affichée étant toujours la même.
2. Simulation Verilog avec Icarus
Le Verilog est un langage de description matériel qui inclut la partie simulation des composants. L’approche classique pour simuler du Verilog est tout naturellement d’utiliser le même langage pour la partie description des stimulus que celui pour la synthèse. Une fois écrit en Verilog, notre banc de test pourra être exécuté par n’importe quels simulateurs du marché.
Le marché est composé de trois principaux simulateurs commerciaux, qui n’aiment pas trop être comparés mutuellement. Certains ont même mis dans leurs licences une clause interdisant de faire des « benchmarks ». On pourrait disserter sur la légalité de ce genre de clause, mais plutôt que de se frotter aux troupeaux d’avocats, nous nous contenterons de les nommer « les trois gros » [5] comme le fait Wilson Snyder dans ses présentations de Verilator.
Heureusement pour nous, il existe également un simulateur Verilog libre nommé Icarus [6]. Ce simulateur, maintenu par Stephen William, est bien installé dans le paysage open source du monde matériel et supporte bien les différents standards du langage. Malgré ce que pourrait laisser penser son site internet (très web95), le projet est toujours activement maintenu.
Que ce soit avec Icarus ou « les trois gros », la simulation est de type « piloté par événements » (event driven). Le simulateur avance par pas. À chaque pas de temps, si le calcul des signaux déclenche d’autres processus, ils seront activés et calculés avant de faire avancer le pas de temps. Cet ordonnancement du calcul est dynamique, il est recalculé à chaque pas de simulation.
Cette manière de faire permet de simuler les pas temporels ainsi que toute la partie « non synthétisable » du Verilog, ce que ne peut pas faire Verilator.
2.1 Installation d’Icarus
Icarus est un logiciel relativement mature, on peut se rabattre sur le paquet de sa distribution. Avec une distribution dérivée de Debian, on utilisera tout simplement apt :
Les sources Verilog du projet disponible sur le dépôt de l’auteur sont stockées dans le répertoire hdl/src :
Le répertoire icarus contient un Makefile qui automatise le lancement des commandes que nous allons décrire dans l’article. En plus de son Makefile, le répertoire verilator contient le fichier source C++ des stimulus du banc de test.
2.2 Banc de test
Pour le moment, nous n’avons écrit que du code Verilog synthétisable pour décrire l’architecture matérielle du composant. Passons maintenant à la simulation du composant via l’écriture d’un banc de test.
L’objectif de la simulation est de générer suffisamment de cycles d’horloge de manière à générer une image VGA complète. Nous allons donc écrire pour cela un « banc de test » (testbench) en Verilog qui va commuter les signaux d’horloge et de reset, et lire les signaux de couleurs en fonction de la synchronisation.
La convention de nommage veut que nous mettions le code du banc dans un fichier du même nom que le composant top suivi de _tb. Nous appellerons donc notre fichier de test RgbVideo_tb.v avec le code ci-dessous que nous allons décrire :
La première ligne du fichier source (0) indique le « pas » de simulation. Ce paramètre donne le temps d’un pas simulé. Le second est une subdivision de ce pas. Ici, le temps de cycle nous importe peu, nous mettrons donc 1 nanoseconde comme nous aurions pu mettre 1 milliseconde, cela ne changera pas le temps de simulation.
On reconnaît un module banc de test (1) à son entête sans port d’entrée sortie. Avant d’instancier le module à tester, nous devons déclarer les signaux (2) dont nous aurons besoin pour connecter ledit module (3). Ici, nous déclarerons les entrées horloge et reset en registres (reg), car c’est le testbench qui va les piloter. Puis, nous déclarerons les sorties du module en fils (wire), car nous nous contenterons de les lire.
En plus de l’instanciation du module top en test, trois tâches indépendantes sont nécessaires pour ce banc.
La première tâche (4) sert à commuter l’horloge. À chaque cycle de simulation #1, le signal clk_i est inversé de manière à générer un cycle complet d’horloge.
La tâche principale (5) décrit le déroulement complet du test en commençant par mettre le module dans son état de reset avec le signal rst_i à « 1 ». On se synchronise ensuite sur le front montant du signal vsync_o pour attendre un cycle complet à « 1 » de manière à capturer une image entière. La tache se termine par l’instruction $finish qui indique au simulateur que la simulation est terminée. Sans cette instruction, et sans avoir indiqué de temps maximum, le simulateur continuerait indéfiniment la simulation.
La tâche principale comporte également les fonctions $dumpfile() et $dumpvars() encadrées par une macro ifdef DUMPVARS. Ce code génère une trace des différents signaux simulés dans un fichier au format VCD. Ce fichier de dump est très pratique pour déverminer le projet. On pourra visualiser les chronogrammes avec le programme GTKWave disponible dans toute bonne distribution GNU/Linux :
Mais cette partie enregistrement monopolise (un peu) de temps de calcul. Pour comparer le temps d’exécution du simulateur avec Verilator, on prendra donc bien soin de la désactiver en commentant la déclaration de la macro DUMPVARS :
Pour finir (6), la dernière tâche va lire les signaux vidéo en sortie du module RgbVideo pour les enregistrer dans un fichier image au format PPM.
Le format PPM
Le format PPM [4] pour Portable PixMap est un format de fichier image enregistré en ASCII (dans notre cas) très pratique pour être lu/écrit avec un simple éditeur de texte. La première ligne donne le format utilisé avec le nombre magique P3.
Ce nombre magique signifie ici que l’on a affaire à une image en couleurs (rouge, vert, bleu), codé en ASCII. Une ligne de commentaire commence par un # et la résolution est donnée par deux nombres donnant respectivement la largeur et la hauteur :
Enfin, la dernière ligne de configuration donne la valeur maximale des couleurs. Comme nous l’avons vu en début d’article, notre module RGB ne génère que deux valeurs de couleurs pour chaque signal, soit « 0 » et « 1 ». La valeur maximum sera donc 1.
Le reste du fichier sera ensuite constitué de groupe de trois valeurs entières séparées par des blancs (espace, tabulation ou retour à la ligne).
L’avantage de ce format est qu’il est facile à manipuler, puisque c’est un simple texte. Le second avantage est que c’est un standard lisible par la plupart des logiciels de visualisation et de traitement d’images. Il sera donc facile de visualiser l’image simulée.
On se synchronise (7) d’abord sur la sortie du reset (front descendant de rst_i) pour ensuite ouvrir le fichier image et y écrire l’entête. Le fichier image est écrit dans le répertoire /tmp/ du système, car ce répertoire est généralement monté dans la mémoire vive de l’ordinateur. On optimise ainsi le temps d’écriture dans le fichier qui se fera dans la RAM.
En (8), la tâche reste en attente tant que le signal de synchronisation verticale est à « 0 ». Puis nous entrons dans la boucle d’écriture de l’image (9) au front montant de vsync_o.
Tant que le signal de synchronisation vertical est actif (à « 1 »), à chaque front de l’horloge, nous écrivons la valeur du pixel dans le fichier (10). À condition que le signal de synchronisation horizontal soit actif également.
Notez que ce banc de test, écrit en pur Verilog, peut être exécuté avec n’importe quel simulateur acceptant le standard. Il n’est pas obligatoire de le faire avec Icarus. On peut tout à fait le faire tourner en l’état avec un des trois gros simulateurs commerciaux du marché pour comparer [5].
Le lecteur ou la lectrice aura remarqué que la programmation d’un banc de test en Verilog « pur » se fait en multitâche. La programmation multitâche n’est pas toujours très facile à déverminer, quand c’est possible on préfère généralement éviter.
L’exécution du banc de test avec Icarus se passe en deux phases. Une première phase de compilation/élaboration avec l’utilitaire iverilog du projet Icarus va générer un fichier exécutable avec l’extension vvp.
L’exécutable RgbVideo_tb.vvp généré, le code du banc de test est maintenant prêt à être exécuté. Le binaire généré peut être lancé directement comme n’importe quel binaire GNU/Linux :
Il peut également être lancé avec l’utilitaire vvp d’Icarus. Cet utilitaire ajoute quelques options d’affichage ainsi que la possibilité de lancer la simulation en mode interactif.
L’image générée par la simulation est visible en figure 3.
Les performances peuvent être mesurées avec l’utilitaire time inclus dans toute bonne distribution GNU/Linux. Pour les besoins de cet article, les mesures ont été faites sur un petit ordinateur « de voyage » de type netbook (Asus E203M avec un Celeron N4000 cadencé à 1,1 GHz et 4 Go de RAM).
Le temps d’élaboration/compilation est de 51 ms, ce qui est très faible.
Le temps de simulation quant à lui est nettement plus conséquent, puisqu’il faut compter 4,57 secondes pour effectuer la simulation complète de l’image.
On parle ici de la simulation de l’affichage d’une image VGA. Le taux de rafraîchissement d’un écran VGA standard est de 60 Hz, si nous voulions simuler une seconde d’affichage, il nous faudrait patienter presque 5 minutes. Même si on réduit le taux de rafraîchissement, il est inenvisageable de simuler une image animée en temps réel. Tout au plus, nous pouvons lancer la simulation (la nuit) et enregistrer la suite d’images pour la rejouer une fois calculée.
Voyons maintenant les performances de Verilator.
3. Verilator
L’histoire de Verilator est donnée dans le manuel utilisateur [verilatorman]. Le logiciel est né en 1994 au sein de l’entreprise DEC (Digital Equipement Corporation). Le code a été libéré en 1998 à l’occasion du rachat de cette partie de l’entreprise par Intel. Wilson Snyder a repris le support en 2001 et a rejoint la Chip Alliance en 2019. La Chip alliance est une organisation intégrée à la fondation Linux qui développe et maintient des logiciels open source pour le développement matériel.
Verilator est donc un outil qui existe depuis très longtemps. Il est naturellement intégré comme « package » dans la plupart des distributions GNU/Linux modernes. Mais Verilator est activement maintenu, et les dernières versions intègrent le calcul parallèle sur les processeurs modernes multicœurs pour des performances décuplées. La compilation et l’installation d’une vue à jour de Verilator étant relativement simples et bien expliquées sur le site officiel [7], on préférera cette méthode plutôt que d’installer le paquet de sa distribution.
Pour les irréductibles de Microsoft, il est tout à fait possible d’utiliser Cygwin ou MinGW pour le compiler et le faire tourner sur le système d’exploitation à fenêtres. La méthode d’installation est également donnée sur le site officiel.
3.1 « Verilation »
La première étape de la simulation avec Verilator est appelée la « verilation » (Verilating). Cette étape consiste à transformer le code Verilog en code C++.
La commande pour « veriler » le module présenté en début d’article est la suivante :
Le linter est très tatillon, ici nous avons deux avertissements (warning), car nous n’utilisons pas tous les bits des compteurs horizontaux et verticaux pour générer l’image.
Le plus simple pour éviter cet avertissement est de l’ignorer spécifiquement pour les deux lignes concernées, puis de le réactiver juste après en modifiant le fichier source rgbvideo.v :
Le message d’erreur est suffisamment bien fait pour que nous connaissions la syntaxe, sans même avoir à nous référer au manuel.
La « verilation » a pour effet de générer une série de fichiers sources C++ dans un répertoire nommé obj_dir/ :
Le nom du module Top est préfixé avec un V pour Verilated.
Les deux principaux fichiers qui nous intéressent ici sont Vrgbvideo.cpp et Vrgbvideo.h qui décrivent le module Verilog converti en C++.
Si l’on ouvre l’entête Vrgbvideo.h, on retrouve les interfaces du module Verilog :
La notation VL_IN8 est une macro déclarée dans le fichier d’entête verilated.h qui définit des variables entières non signées de largeur 8 bits maximum. Les variables membres publiques de la classe Vrgbvideo données ci-dessus représentent les entrées (VL_INx) et sorties (VL_OUTx) du module Verilog. Comme nous allons le voir par la suite, elles pourront être lues et écrites au moyen d’assignation C++ (=).
La classe possède également des méthodes publiques pour créer l’objet et évaluer les sorties en fonction des entrées :
La méthode importante à retenir ici est la fonction d’évaluation eval() qui lorsqu’elle est appelée calcule les valeurs des sorties en fonction des entrées données.
Tout ce code se présente comme une classe « indépendante » que nous allons devoir instancier dans une fonction main avec notre code de test.
Le manuel de référence propose un exemple minimum de code C++ pour instancier le modèle [8]. Nous allons l’adapter à notre exemple rgbvideo dans un fichier source appelé sim_main.cpp :
En plus des entêtes propres à la programmation C++, nous avons besoin d’inclure les routines Verilator avec le fichier verilated.h (1) ainsi que la définition de notre module « verilé » (2).
La classe SimMain est une classe « maison » qui va encapsuler tous les méthodes et attributs propres à la simulation.
On stocke le module VRgbVideo dans le pointeur *top (4) et le temps simulé est compté avec la variable main_time sur 64 bits (5).
La méthode step() (6) va servir quant à elle à faire avancer la simulation du nombre de cycles voulu. C’est cette dernière qui va nous intéresser plus particulièrement (7). Dans cette fonction, qui est le cœur de la simulation, on va principalement s’occuper de l’horloge en deux parties.
Avec la fonction low_cycle(), on passe d’abord la valeur de l’horloge à « 0 » (8), puis on évalue le modèle avec l’appel à la fonction d’évaluation top->eval(). On lit ensuite les valeurs des pixels avec la fonction read_pixel() puis on passe la valeur d’entrée de l’horloge à « 1 » avec la fonction high_cycle().
La fonction read_pixel() est en fait la partie où nous allons enregistrer les pixels de l’image au format PPM (9) de la même manière que nous l’avons fait en Verilog initialement.
Dump VCD
Il est tout à fait possible de générer des traces au format VCD avec Verilator. Traces qui seront visualisable avec GTKWave. Verilator inclut tout ce qu’il faut pour enregistrer un fichier de traces au format désiré à condition d’ajouter l’option --trace à la commande de « verilation ».
On intègre ensuite le fichier d’entête verilated_vcd_c.h (15). Puis on ajoute un objet VerilatedVcdC (16) en attribut à notre classe de test.
On initialise l’objet (17) et on le passe en paramètre de la méthode trace(). Le second paramètre est un entier donnant la profondeur hiérarchique du « dump ».
De la même manière que pour la fonction d’évaluation qui fait avancer la simulation, c’est à nous de « dumper » les valeurs des signaux en appelant la fonction dump() à chaque cycle de simulation (18). L’entier passé en paramètre correspond au numéro de cycle (le temps sans unité) qui sera inscrit dans le VCD.
Le « dump » des signaux dans un fichier VCD est très gourmand en ressources processeur ainsi qu’en mémoire. Les fichiers deviennent très vite volumineux. C’est pourquoi nous prendrons bien soin de garder une option de compilation (19) sans le dump VCD si l’on souhaite améliorer les performances de la simulation.
Le fichier ainsi généré se lit ensuite avec le fameux visualiseur libre GTKWave :
Pour finir, l’écriture du banc de test à proprement parler se trouve dans la fonction principale main() (10).
On instancie tout d’abord le module à tester dans une variable que nous nommerons top. On passe ensuite l’adresse de cette variable au constructeur de notre objet de la classe SimMain() que nous venons de définir.
Et nous commençons par nous assurer que le module commence bien par un état de reset en fixant la valeur de top.rst_i à « 1 » pendant 5 cycles d’horloge.
La suite est similaire au process principal du banc de test en Verilog :
- (12) on boucle en attendant que le signal vsync_o passe à « 1 » ;
- (13) on boucle à nouveau sur l’état « 1 » de vsync_o le temps d’enregistrer une image entière ;
- (14) enfin, on termine le test en affichant le nombre de cycles simulés et le nombre de lignes de l’image enregistrées.
On notera qu’ici tout le code est linéaire et non multitâche, comme on l’a vu dans le banc de test Verilog. On aurait pu se servir des librairies standard du C++ pour écrire un banc de test multitâche. L’intérêt dans cet exemple est relativement minime, d’autant que pour faire avancer le pas de simulation, on doit nécessairement en passer par la fonction d’évaluation. De plus, la règle dans la programmation multitâche, c’est que quand on peut s’en passer, il vaut mieux éviter pour limiter les bugs pénibles.
Verilator permet de « veriler », puis de compiler les sources générées avec les sources du testbench fournies en une seule commande. On y ajoutera la commande time pour mesurer le temps total d’élaboration/compilation.
Le temps de compilation de plus de 5 secondes n’est vraiment pas négligeable, mais assez classique pour du C++. Ce temps est une centaine de fois plus long que le temps d’élaboration d’Icarus. C’est pourquoi on réservera toujours la simulation avec Verilator pour les gros projets, pour les tests unitaires, on préférera toujours utiliser Icarus.
Le binaire généré se trouve dans le répertoire obj_dir/ et se lance directement. De la même manière que la compilation, nous le lancerons avec la commande time pour mesurer le temps de simulation :
Le temps de simulation de 154 ms est tout de même une trentaine de fois plus rapide qu’avec Icarus. Sans aucune optimisation de Verilator et en tournant sur un seul thread.
Le manuel donne des options permettant d’optimiser la compilation. Avec ces options, on peut encore gagner une petite dizaine de millisecondes. La programmation multitâche pourrait quant à elle diminuer furieusement le temps d’exécution de la simulation sur des projets complexes, mais elle nécessiterait un article supplémentaire pour la détailler.
4. Pour conclure
Le tableau suivant résume les performances d’Icarus et de Verilator sur un petit ordinateur muni d’un Celeron N4000 cadencé à 1,1 GHz et 4 Go de RAM :
Étape logicielle |
Icarus |
Verilator |
Rapport Icarus/Verilator |
Compilation/élaboration |
0,051 s |
5,321 s |
Verilator/Icarus ~ 100 |
Simulation |
4,568 s |
0,154 s |
Icarus/Verilator ~ 30 |
Bien sûr, d’un premier coup d’œil en regardant ces chiffres, on se demande l’intérêt de Verilator. Il met tellement de temps à compiler qu’on a plus vite fait de simuler notre Verilog avec Icarus. En plus, le Verilog est portable et on pourra utiliser d’autres simulateurs, si besoin !
Mais la simulation que nous venons d’effectuer est relativement petite. Le taux de rafraîchissement normal du VGA est de 60 Hz, si nous voulions simuler une seconde d’affichage avec Icarus il nous faudrait un peu moins de 5 minutes de simulation, alors qu’avec Verilator, en moins de 10 secondes, c’est plié. Avec Verilator, il est même possible d’exécuter la simulation en temps réel.
En effet, si l’on abaisse le rafraîchissement à 5/6 images secondes, on peut « faire tourner » le modèle en simulation sur son ordinateur et voir les animations directement. Ce qui est inenvisageable avec Icarus, on ne va pas attendre plus de 4 secondes de rafraîchissement entre chaque image.
L’exemple de cet article a été simulé avec un tout petit ordinateur et très peu d’optimisations. Avec un code Verilog plus complexe et un banc de test qui tirerait parti de la puissance de calcul en multicœur, il est possible de décupler la puissance de Verilator ! On parle de temps de simulation jusqu’à 500 fois plus rapide par rapport à Icarus.
Il est également possible de simuler des processeurs écrits en Verilog et de s’y connecter avec un terminal pour le piloter directement. À en croire le site officiel de Verilator, c’est d’ailleurs le simulateur qui est utilisé par tous les grands noms du microprocesseur, de ARM à Intel, en passant par SiFive ou Microchip.
Enfin, Verilator est très utile pour développer des modules de traitement du signal dans un FPGA. Même s’il n’est pas traité en « temps réel » en simulation, il est tout de même envisageable de faire tourner le code avec des échantillons réels de signaux et d’analyser le résultat assez rapidement. Suffisamment en tout cas pour pouvoir enchaîner les cycles de modification-simulation-analyse-déverminage, sans pour autant y passer des mois en laissant les simulations tourner chaque nuit.
Ces performances fulgurantes pourraient donner envie de jeter son simulateur habituel et de tout faire avec Verilator.
Cela serait une erreur. En effet, Verilator vient tout de même avec son lot d’inconvénients. Et le premier d’entre eux : il n’est pas capable de gérer le temps, la directive timescale est complètement ignorée par Verilator, et il ne sait que faire des délais #n donnés dans le banc de test, mais également dans certains modèles de macro interne aux FPGA, par exemple.
Comme on l’a vu, le temps de compilation est particulièrement important. Ce temps peut certes être réduit sur des ordinateurs modernes en jouant sur l’option -jx pour générer des Makefiles qui compilent les fichiers sources en parallèle. Comme on a un minimum de deux fichiers sources (le module « verilé » et le testbench « main »), on y gagne toujours un peu. Mais cela reste long, surtout si l’on passe son temps à recompiler le modèle pour effectuer des tests unitaires ciblés en fonction de paramètres de configuration du module.
Verilator génère des modèles C++ « secs », toute la partie pilotage des signaux et moniteurs est à réécrire à chaque nouveau projet. Il n’existe pas de librairie standard permettant de simuler un bus SPI ou I²C, par exemple, pas plus que des pilotes de bus Wishbone ou AXI-Lite. Le choix de la développeuse ou du développeur (multi ou mono thread, découpage des cycles d’horloge…) de l’appel à la fonction d’évaluation conditionnera toute son « API » utilisée dans les librairies. Bref, à chaque nouveau projet, on en est souvent réduit à réécrire les « librairies ».
La possibilité de générer un modèle en SystemC corrige un peu ce problème, mais il n’existe pas non plus de librairie de test en SystemC.
De plus, il n’est pas évident que les équipes de développement en Verilog aient une maîtrise du langage C++. Les équipes de développement qui font du Verilog sont généralement issues du monde de l’électronique, alors que le C++ est une compétence de développement logicielle. Les deux mondes sont très différents et ne se comprennent pas toujours.
Enfin, le dernier problème de Verilator est qu’il permet de simuler… du Verilog. Or, ça n’est pas le seul langage utilisé pour la conception numérique sur FPGA, il y a également VHDL. Cet inconvénient est cependant en phase de résolution. Il est en effet possible aujourd’hui de convertir du VHDL en Verilog avec le simulateur GHDL [9] soit en utilisant directement l’option de synthèse interne au programme, soit en passant par l’extension yosys-ghdl-plugin [10] de Yosys. La conversion est aujourd’hui assez mature pour récupérer un modèle Verilog qui garde les noms des signaux et que l’on peut ensuite simuler avec Verilator.
Références
[1] Verilator, https://www.veripool.org/verilator/
[2] Une carte pilote de LED RGB hackée en kit de développement FPGA à bas coût, Fabien Marteau, Hackable 35 - https://connect.ed-diamond.com/Hackable/hk-035/une-carte-pilote-de-led-rgb-hackee-en-kit-de-developpement-fpga-a-bas-cout
[3] Les sources de l’article, https://github.com/Martoni/Diamond_HK_GLMF_OS
[4] Format de fichier texte PPM, http://netpbm.sourceforge.net/doc/ppm.html
[5] Modelsim (Siemens EDA ex Mentor Graphics), Incisive (Cadence) et VCS (Synopsys).
[6] Icarus Verilog, http://iverilog.icarus.com/
[verilatorman] Manuel de Verilator, https://verilator.org/guide/latest/
[7] Installation de Verilator, https://verilator.org/guide/latest/install.html
[8] Modèle de fonction main, https://verilator.org/guide/latest/connecting.html#connecting-to-c
[9] GHDL, le simulateur VHDL libre, https://github.com/ghdl/ghdl
[10] Extension de Yosys permettant de « connecter » GHDL, https://github.com/ghdl/ghdl-yosys-plugin