{"id":278,"date":"2015-03-21T17:21:16","date_gmt":"2015-03-21T16:21:16","guid":{"rendered":"http:\/\/www.fabienm.eu\/flf\/?p=278"},"modified":"2015-03-23T08:20:00","modified_gmt":"2015-03-23T07:20:00","slug":"icarus-vs-verilator","status":"publish","type":"post","link":"https:\/\/www.fabienm.eu\/flf\/icarus-vs-verilator\/","title":{"rendered":"Icarus vs Verilator"},"content":{"rendered":"<p>Plusieurs solutions de simulations s&rsquo;offrent \u00e0 nous quand on d\u00e9veloppe un composant en Verilog. On peut \u00e9crire le testbench en Verilog, de mani\u00e8re \u00e0 \u00eatre compatible avec la plupart des simulateurs du march\u00e9. Dans ce cas, le simulateur libre le plus c\u00e9l\u00e8bre est <a href=\"http:\/\/iverilog.icarus.com\/\">Icarus.<\/a><\/p>\n<p>Mais la solution du \u00abtout Verilog\u00bb est relativement lente en temps de simulation, et l&rsquo;utilisation du langage Verilog est restrictive,\u00a0 en effet il n&rsquo;est pas facile de s&rsquo;approprier toutes les subtilit\u00e9s du langage.<\/p>\n<p>C&rsquo;est l\u00e0 que la solution de <a href=\"http:\/\/www.veripool.org\/wiki\/verilator\">Verilator<\/a> devient tr\u00e8s int\u00e9ressante. Verilator permet de convertir un mod\u00e8le de composant \u00e9crit en Verilog <strong>synth\u00e9tisable<\/strong> en un mod\u00e8le C++. De cette mani\u00e8re, \u00e9crire le code du testbench revient \u00e0 instancier notre composant dans un main() en C++ et \u00e0 d\u00e9crire nos tests avec toute la libert\u00e9 qu&rsquo;offre ce langage.<\/p>\n<p>Mieux encore, il est possible d&rsquo;\u00e9crire notre testbench en <a href=\"http:\/\/fr.wikipedia.org\/wiki\/SystemC\">SystemC<\/a>, et de profiter ainsi de cette librairie con\u00e7ue pour la simulation de circuit num\u00e9rique.<\/p>\n<p>L&rsquo;id\u00e9e va \u00eatre ici de mesurer le temps effectif de simulation de chacune de ses deux solutions. On se servira pour cela du composant d&rsquo;antirebond du projet \u00abblinking led project\u00bb (blp) disponible <a href=\"https:\/\/github.com\/Martoni\/blp\/tree\/master\/verilog\">sur github<\/a>.<\/p>\n<p><strong>Le composant synth\u00e9tisable<\/strong><\/p>\n<p>Le composant <a href=\"https:\/\/github.com\/Martoni\/blp\/blob\/master\/verilog\/src\/button_deb.v\">button_deb<\/a> commute sa sortie<em> button_valid<\/em> \u00e0 chaque front montant du signal d&rsquo;entr\u00e9e<em> button_in<\/em>. Pour que le fonctionnement soit un peu plus complexe qu&rsquo;une simple commutation, et surtout pour bien coller au fonctionnement r\u00e9el, le composant est muni d&rsquo;un antirebond de 20 ms (par d\u00e9faut). Quand le premier front survient, un compteur est d\u00e9clench\u00e9 et aucun autre front n&rsquo;est pris en compte pendant 20 ms.<\/p>\n<p>Voici ce que cela donne avec <a href=\"http:\/\/gtkwave.sourceforge.net\/\">gtkwave<\/a>:<\/p>\n<figure id=\"attachment_285\" aria-describedby=\"caption-attachment-285\" style=\"width: 604px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2015\/03\/blp_button_deb.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-285 size-large\" src=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2015\/03\/blp_button_deb-1024x307.png\" alt=\"blp_button_deb\" width=\"604\" height=\"181\" srcset=\"https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2015\/03\/blp_button_deb-1024x307.png 1024w, https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2015\/03\/blp_button_deb-300x90.png 300w, https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2015\/03\/blp_button_deb.png 1362w\" sizes=\"auto, (max-width: 604px) 100vw, 604px\" \/><\/a><figcaption id=\"caption-attachment-285\" class=\"wp-caption-text\">Chronogramme du testbench avec GTKwave<\/figcaption><\/figure>\n<p><strong>Le testbench verilog (Icarus)<\/strong><\/p>\n<p>Tout comme en VHDL, un testbench en Verilog se pr\u00e9sente sous la forme d&rsquo;un module sans entr\u00e9e\/sortie.<\/p>\n<p>La premi\u00e8re chose \u00e0 d\u00e9finir est le temps, avec la directive `timescale:<\/p>\n<pre><code>`timescale 1ns\/100ps;<\/code><\/pre>\n<p>Le premier chiffre indique le pas de simulation, cela correspondra au temps d&rsquo;attente que l&rsquo;on retrouvera tout au long du code avec l&rsquo;\u00abinstruction\u00bb &lsquo;#&rsquo;.\u00a0 Le deuxi\u00e8me nombre indique la pr\u00e9cision maximum.<\/p>\n<p>Par exemple, pour simuler l&rsquo;horloge \u00e0 95Mhz on \u00e9crira :<\/p>\n<pre><code>\/* Make a regular pulsing clock. *\/\r\nalways\r\n    #5.263158 clk = !clk;\r\n<\/code><\/pre>\n<p>Mais comme la pr\u00e9cision indiqu\u00e9e en d\u00e9but de code est de 100ps, seule le 5.2 sera pris en compte pour la simulation, ce qui dans notre cas est tout \u00e0 fait suffisant.<\/p>\n<p>La simulation de l&rsquo;appui sur le bouton avec rebond se fait ensuite dans un process que l&rsquo;on appel souvent \u00abstimulis\u00bb par convention :<\/p>\n<pre><code>\/* Stimulis *\/\r\ninitial begin\r\n    $display(\"begin stimulis\");\r\n    $dumpfile(\"simu\/button_deb_tb.vcd\");\r\n    $dumpvars(1, clk, rst, button_in, button_valid, button);\r\n    $monitor(\"At time %t, value = %h (%0d)\",\r\n        $time, button_in, button_in);\r\n[...]\r\nend\r\n<\/code><\/pre>\n<p>Le Verilog fourni tout un tas de primitive permettant de simplifier le debuggage. Notamment $display() et $monitor(), pour afficher du texte pendant la simulation. $display() ne fait qu&rsquo;afficher du texte au moment o\u00f9 la fonction est appel\u00e9e, alors que monitor va afficher le texte \u00e0 chaque changement d&rsquo;\u00e9tat de ses param\u00e8tres.<\/p>\n<p>Les functions $dump* permettent de d\u00e9finir le format de fichiers des traces ainsi que les signaux a dumper. Dans ce cas pr\u00e9cis on choisira le format vcd qui est un format non compress\u00e9, de mani\u00e8re \u00e0 am\u00e9liorer la comparaison avec verilator qui lui ne sait faire que du vcd. Mais cela g\u00e9n\u00e8re vite de tr\u00e8s gros fichiers, il sera pr\u00e9f\u00e9rable d&rsquo;utiliser le format compress\u00e9 lxt2, fst ou fsdb pour des simulations plus longues.<\/p>\n<p>Pour factoriser un peu de code d&rsquo;attente on d\u00e9crit des t\u00e2ches d&rsquo;attentes wait_ms et wait_us :<\/p>\n<pre><code>\r\n\/* some usefull functions *\/\r\ntask wait_us;\r\n    input integer another_time;\r\n    begin\r\n        repeat(another_time) begin\r\n            # 1_000;\r\n        end\r\n    end\r\nendtask\r\n\r\ntask wait_ms;\r\n[...]\r\n<\/code><\/pre>\n<p>T\u00e2ches qui seront appel\u00e9es lors de la simulation des rebonds :<\/p>\n<pre><code>[...]\r\n        button_in = 1;\r\n        wait_us(`DEBOUNCE_PER_MS * 8);\r\n        button_in = 0;\r\n        wait_us(`DEBOUNCE_PER_MS * 8);\r\n        button_in = 1;\r\n        wait_us(`DEBOUNCE_PER_MS * 10);\r\n        button_in = 0;\r\n        wait_us(`DEBOUNCE_PER_MS * 10);\r\n        button_in = 1;\r\n\r\n        wait_ms(`DEBOUNCE_PER_MS * 2);\r\n[...]\r\n<\/code><\/pre>\n<p>Pour lancer notre testbench avec Icarus, la premi\u00e8re chose \u00e0 faire est de compiler le code au moyen de la commande suivante :<\/p>\n<pre><code>iverilog -o simu\/button_deb test\/test_button_deb.v src\/button_deb.v\r\n<\/code><\/pre>\n<p>Icarus va ainsi cr\u00e9er un binaire ex\u00e9cutable nomm\u00e9 simu\/button_deb que l&rsquo;on lancera avec les arguments de dumps:<\/p>\n<pre><code>vvp simu\/button_deb -lvcd\r\n<\/code><\/pre>\n<p>Le fichier de traces (VCD) g\u00e9n\u00e9r\u00e9 fait une taille v\u00e9n\u00e9rable de 752Mo et peut \u00eatre visualis\u00e9 avec gtkwave en l&rsquo;indiquant simplement en param\u00e8tre de la commande :<\/p>\n<pre><code>gtkwave simu\/button_deb_tb.vcd\r\n<\/code><\/pre>\n<p>Sur un Lenovo T430, la simulation prend <strong>1 minute et 17 secondes<\/strong>, ce qui est tout de m\u00eame relativement lent pour une simulation d\u2019appui sur un boutton \ud83d\ude09<\/p>\n<p>Voyons maintenant ce que nous donne Verilator.<\/p>\n<p><strong>Le testbench C++<\/strong><\/p>\n<p>Un testbench Verilator se pr\u00e9sente sous la forme d&rsquo;un main() C++. Dans le main() du testbench nous instancierons l&rsquo;objet correspondant au model verilog transform\u00e9 par verilator.<br \/>\nPour cela nous devons donc convertir notre bouton_deb.v en C++ au moyen de la commande Verilator suivante :<\/p>\n<pre><code>verilator -Wall -cc src\/button_deb.v --trace --exe test\/test_button_deb.cpp\r\n<\/code><\/pre>\n<p>Cette commande va nous cr\u00e9er un projet avec le code source du mod\u00e8le ainsi que le makefile pour compiler. Il suffira donc de se rendre dans le r\u00e9pertoire obj_dir et de faire \u00abmake\u00bb pour compiler le mod\u00e8le.<\/p>\n<p>Il ne nous restera plus qu&rsquo;\u00e0 instancier notre bouton dans notre testbench test_button_deb.cpp :<\/p>\n<pre><code>    Vbutton_deb* top = new Vbutton_deb;\r\n<\/code><\/pre>\n<p>Ainsi que l&rsquo;objet tfp pour les dump VCD:<\/p>\n<pre><code>    VerilatedVcdC* tfp = new VerilatedVcdC;<\/code><\/pre>\n<p>Pour simuler notre objet \u00abtop\u00bb il faut assigner des valeurs aux signaux d&rsquo;entr\u00e9es :<\/p>\n<pre><code>    top-&gt;rst = 1;\r\n    top-&gt;button_in = 0;\r\n    top-&gt;clk = 0;\r\n<\/code><\/pre>\n<p>Puis \u00e9valuer les sorties avec la m\u00e9thode eval() :<\/p>\n<pre><code>        top-&gt;eval();\r\n<\/code><\/pre>\n<p>\u00c0 chaque front d&rsquo;horloge il faut donc changer la valeur de clk et \u00e9valuer :<\/p>\n<pre><code>        top-&gt;clk = !top-&gt;clk;\r\n        top-&gt;eval();\r\n<\/code><\/pre>\n<p>Dans tout ce que nous venons de voir le temps n&rsquo;intervient pas. En r\u00e9alit\u00e9, le temps n&rsquo;est tout simplement pas g\u00e9r\u00e9 dans les mod\u00e8les Verilator, le mod\u00e8le ne fait qu&rsquo;\u00e9valuer les sorties en fonction des entr\u00e9es, c&rsquo;est \u00e0 nous de g\u00e9rer le temps comme nous le souhaitons.<\/p>\n<p>Nous allons donc g\u00e9rer le temps au moment du dump des signaux en lui indiquant le temps en argument:<\/p>\n<pre><code>        tfp-&gt;dump(10);\r\n<\/code><\/pre>\n<p>Pour \u00e9viter d&rsquo;avoir \u00e0 taper tout \u00e7a \u00e0 chaque fois on pourra cr\u00e9er une fonction \u00abtime_pass\u00bb qui fait passer le temps, avec un compteur global pour incr\u00e9menter le temps:<\/p>\n<pre><code>#define BASE_TIME_NS ((1000*1000)\/(CLK_FREQ*2))\r\nint base_time = 0;\r\n\r\n\/* the time is passing *\/\r\nvoid time_pass(VerilatedVcdC *tfp, Vbutton_deb *top) {\r\n        top-&gt;clk = !top-&gt;clk;\r\n        tfp-&gt;dump(base_time*BASE_TIME_NS);\r\n        top-&gt;eval();\r\n        base_time++;\r\n}\r\n<\/code><\/pre>\n<p>\u00c0 l&rsquo;image du testbench verilog, on pourra aussi cr\u00e9er des fonctions d&rsquo;attente wait_ms et wait_us:<\/p>\n<pre><code>\/* wait for us *\/\r\nvoid wait_us(VerilatedVcdC *tfp, Vbutton_deb *top, int timeus) {\r\n    int wait_time = 0;\r\n    while((wait_time * BASE_TIME_NS) &lt; (timeus * 1000)) {\r\n        wait_time++;\r\n        time_pass(tfp, top);\r\n    }\r\n}\r\n\r\n\/* wait for ms *\/\r\nvoid wait_ms(VerilatedVcdC *tfp, Vbutton_deb *top, int timems) {\r\n    int wait_time = 0;\r\n    while((wait_time * BASE_TIME_NS) &lt; (timems * 1000 * 1000)) {\r\n        wait_time++;\r\n        time_pass(tfp, top);\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Le code simulant les rebonds du bouton ressemble ainsi \u00e0 s&rsquo;y m\u00e9prendre au code Verilog:<\/p>\n<pre><code>[...]\r\n        top-&gt;button_in = 1;\r\n        wait_us(tfp, top, DEBOUNCE_PER_MS);\r\n        top-&gt;button_in = 0;\r\n        wait_us(tfp, top, DEBOUNCE_PER_MS);\r\n        top-&gt;button_in = 1;\r\n        wait_us(tfp, top, DEBOUNCE_PER_MS * 5);\r\n        top-&gt;button_in = 0;\r\n        wait_us(tfp, top, DEBOUNCE_PER_MS * 5);\r\n        top-&gt;button_in = 1;\r\n        wait_us(tfp, top, DEBOUNCE_PER_MS * 8);\r\n        top-&gt;button_in = 0;\r\n        wait_us(tfp, top, DEBOUNCE_PER_MS * 8);\r\n        top-&gt;button_in = 1;\r\n        wait_us(tfp, top, DEBOUNCE_PER_MS * 10);\r\n        top-&gt;button_in = 0;\r\n        wait_us(tfp, top, DEBOUNCE_PER_MS * 10);\r\n        top-&gt;button_in = 1;\r\n\r\n        wait_ms(tfp, top, DEBOUNCE_PER_MS * 2);\r\n[...]\r\n<\/code><\/pre>\n<p>Pour lancer la simulation on va d&rsquo;abord compiler le tout :<\/p>\n<pre><code>make -C obj_dir\/ -j -f Vbutton_deb.mk Vbutton_deb\r\n<\/code><\/pre>\n<p>Puis le lancer simplement comme un vulgaire binaire ex\u00e9cutable :<\/p>\n<pre><code>.\/Vbutton_deb\r\n<\/code><\/pre>\n<p>La simulation cr\u00e9e un fichier VCD de 654Mo que nous pouvons visualiser avec gtkwave.<br \/>\nSur le m\u00eame Lenovo T430 la simulation ne dure cette fois que <strong>17 secondes<\/strong>, ce qui est tr\u00e8s nettement plus rapide qu&rsquo;Icarus !<\/p>\n<p>Mieux, si on optimise le code \u00e0 la compilation avec l&rsquo;option -O3 :<\/p>\n<pre><code>verilator -Wall -cc src\/button_deb.v --trace <strong>-O3<\/strong> -noassert --exe test\/test_button_deb.cpp\r\n<\/code><\/pre>\n<p>Le temps de simulation tombe \u00e0 <strong>3 secondes<\/strong> !<\/p>\n<p>La rapidit\u00e9 d&rsquo;ex\u00e9cution est telle que certain r\u00e9ussissent \u00e0 faire \u00abtourner\u00bb un soft-core avec son programme, \u00e0 des fr\u00e9quences allant jusqu\u2019\u00e0 la centaine de kilohertz.<\/p>\n<p><strong>Formidable !<\/strong><\/p>\n<p>C&rsquo;est un peu l&rsquo;expression que l&rsquo;on a lors des premiers tests de verilator, pouvoir faire de la simulation 20 fois plus rapidement qu&rsquo;Icarus semble formidable. Surtout quand cela passe par de la simplicit\u00e9 d&rsquo;\u00e9criture du testbench en C++ ou SystemC.<\/p>\n<p>N\u00e9anmoins il faut relativiser un peu notre ferveur, Verilator a encore un gros point noir: il ne g\u00e8re pas le temps. Verilator n&rsquo;est capable que de g\u00e9rer des mod\u00e8le Verilog <strong>synth\u00e9tisable. <\/strong>Mais si ce code synth\u00e9tisable inclu des primitives du constructeurs : RAM, Multiplieur, &#8230; et surtout PLL. Verilator ne sera pas capable de les simuler.<\/p>\n<p>En ce qui concerne les Ram ou les multiplieurs cela ne pose pas trop de probl\u00e8me dans la mesure ou il est assez simple de les<a href=\"http:\/\/danstrother.com\/2010\/09\/11\/inferring-rams-in-fpgas\/\"> inf\u00e9rer<\/a>. Mais pour les PLL cela devient bien plus compliqu\u00e9, et je ne parle m\u00eame pas des composants sp\u00e9cifique au constructeur (Bus serie, transceiver, core, &#8230;).<\/p>\n<p>Ce \u00abbug\u00bb a \u00e9t\u00e9 r\u00e9pertori\u00e9<a href=\"http:\/\/www.veripool.org\/issues\/223-Verilator-Support-behavioral-PLLs-Events\"> il y 5 ans <\/a>d\u00e9j\u00e0 mais pour l&rsquo;instant personne ne semble s&rsquo;\u00eatre attel\u00e9 \u00e0 la t\u00e2che. D&rsquo;apr\u00e8s un des auteur de Verilator ajouter cette fonctionnalit\u00e9 devrait prendre quelques mois de programmation et de test. Mais cela vaudrait quand m\u00eame le coup !<\/p>\n<p>Alors qui s&rsquo;y colle ?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Plusieurs solutions de simulations s&rsquo;offrent \u00e0 nous quand on d\u00e9veloppe un composant en Verilog. On peut \u00e9crire le testbench en Verilog, de mani\u00e8re \u00e0 \u00eatre compatible avec la plupart des simulateurs du march\u00e9. Dans ce cas, le simulateur libre le plus c\u00e9l\u00e8bre est Icarus. Mais la solution du \u00abtout Verilog\u00bb est relativement lente en temps &hellip; <a href=\"https:\/\/www.fabienm.eu\/flf\/icarus-vs-verilator\/\" class=\"more-link\">Continuer la lecture de <span class=\"screen-reader-text\">Icarus vs Verilator<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","footnotes":""},"categories":[3,27,20],"tags":[31,28,29,30],"class_list":["post-278","post","type-post","status-publish","format-standard","hentry","category-langages","category-verilator","category-verilog","tag-icarus","tag-simulation","tag-verilator","tag-verilog"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"post-thumbnail":false},"uagb_author_info":{"display_name":"Fabien Marteau","author_link":"https:\/\/www.fabienm.eu\/flf\/author\/admin\/"},"uagb_comment_info":0,"uagb_excerpt":"Plusieurs solutions de simulations s&rsquo;offrent \u00e0 nous quand on d\u00e9veloppe un composant en Verilog. On peut \u00e9crire le testbench en Verilog, de mani\u00e8re \u00e0 \u00eatre compatible avec la plupart des simulateurs du march\u00e9. Dans ce cas, le simulateur libre le plus c\u00e9l\u00e8bre est Icarus. Mais la solution du \u00abtout Verilog\u00bb est relativement lente en temps\u2026","_links":{"self":[{"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/278","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/comments?post=278"}],"version-history":[{"count":23,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/278\/revisions"}],"predecessor-version":[{"id":308,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/278\/revisions\/308"}],"wp:attachment":[{"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/media?parent=278"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/categories?post=278"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/tags?post=278"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}