{"id":1641,"date":"2020-12-17T19:56:49","date_gmt":"2020-12-17T18:56:49","guid":{"rendered":"http:\/\/www.fabienm.eu\/flf\/?p=1641"},"modified":"2020-12-17T19:56:49","modified_gmt":"2020-12-17T18:56:49","slug":"portage-de-taptempo-en-vhdl","status":"publish","type":"post","link":"https:\/\/www.fabienm.eu\/flf\/portage-de-taptempo-en-vhdl\/","title":{"rendered":"Portage de TapTempo en VHDL"},"content":{"rendered":"\n<p><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl\"><strong>[D\u00e9p\u00eache publi\u00e9e initialement sur LinuxFr]<\/strong><\/a><\/p>\n\n\n\n<p>Ayant pr\u00e9par\u00e9 tout le mat\u00e9riel pour faire du <a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog\">TapTempo en Verilog<\/a>, il \u00e9tait trop tentant de r\u00e9aliser la m\u00eame chose en\u00a0<a href=\"https:\/\/fr.wikipedia.org\/wiki\/VHDL\">VHDL<\/a>. L\u2019occasion de se plonger dans ce langage de description mat\u00e9riel concurrent du Verilog.<br>L\u2019occasion \u00e9galement de parler des avanc\u00e9es de GHDL, un simulateur libre du VHDL, et d\u00e9sormais \u00e9galement capable de faire la synth\u00e8se en conjonction avec Yosys.<\/p>\n\n\n\n<p>Pour comprendre TapTempo dans la culture <a href=\"https:\/\/linuxfr.org\/wiki\/moule\">moulesque<\/a> de LinuxFr.org, il est conseill\u00e9 d\u2019aller faire un petit tour sur la <a href=\"https:\/\/linuxfr.org\/wiki\/taptempo\">page wiki homonyme<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sommaire<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-le-vhdl\">Le VHDL<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-architecture-de-taptempo\">Architecture de TapTempo<\/a><ul><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-les-param%C3%A8tres-dans-un-package--taptempo_pkg\">Les param\u00e8tres dans un package : <\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/taptempo_pkg.vhd\">taptempo_pkg<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-un-cadenceur-timer-pour-tout-le-syst%C3%A8me--timepulse\">Un cadenceur (timer) pour tout le syst\u00e8me : <\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/timepulse.vhd\">Timepulse<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-filtrage-des-rebonds--debounce\">Filtrage des rebonds : <\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/debounce.vhd\">debounce<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-mesure-de-la-p%C3%A9riode-dappuis--percount\">Mesure de la p\u00e9riode d\u2019appuis : <\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/percount.vhd\">percount<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-conversion-p%C3%A9riodefr%C3%A9quence-division--per2bpm\">Conversion p\u00e9riode\/fr\u00e9quence (division) : <\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/per2bpm.vhd\">per2bpm<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-sortie-affichage-en-rapport-cyclique--pwmgen\">Sortie \u00abaffichage\u00bb en rapport cyclique : <\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/pwmgen.vhd\">pwmgen<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-synth%C3%A8se-placement-routage-et-configuration\">Synth\u00e8se, placement-routage et configuration<\/a><ul><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-ghdl--yosys-la-lune-de-miel\">GHDL + Yosys, la lune de miel<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-placement-routage-avec-nextpnr\">Placement routage avec NextPnR<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-en-avant-la-musique-avec-openfpgaloader\">En avant la musique avec openFPGALoader<\/a><\/li><\/ul><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/portage-de-taptempo-en-vhdl#toc-conclusion\">Conclusion<\/a><\/li><\/ul>\n\n\n\n<p>Comme l\u2019indique le titre de cette d\u00e9p\u00eache, nous allons effectuer un \u00ab&nbsp;portage&nbsp;\u00bb du projet TapTempo, qui \u00e9tait \u00e9crit en Verilog, vers le language VHDL. Nous aurons donc l\u2019avantage de profiter d\u2019une architecture d\u00e9j\u00e0 con\u00e7ue. Et comme une partie des stimulus de simulation (banc de test) ont \u00e9t\u00e9 \u00e9crit en Python avec <a href=\"https:\/\/github.com\/cocotb\/cocotb\">CocoTB<\/a>, nous allons pouvoir les reprendre (presque) tels quels pour simuler notre version VHDL.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/img.linuxfr.org\/img\/687474703a2f2f66616269656e6d2e65752f706172746167652f74617074656d706f636f6c6f726c696768742e4a5047\/taptempocolorlight.JPG\" alt=\"Vue d\u2019ensemble du mat\u00e9riel pour TapTempo\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"toc-le-vhdl\">Le VHDL<\/h2>\n\n\n\n<p>Le VHDL \u2013 pour <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Very_High_Speed_Integrated_Circuit\"><strong>V<\/strong>HSIC<\/a> <strong>H<\/strong>ardware <strong>D<\/strong>escription <strong>L<\/strong>anguage \u2013 est un langage de description mat\u00e9rielle issu d\u2019une commande du minist\u00e8re de la D\u00e9fense am\u00e9ricaine, c\u2019est donc en toute logique qu\u2019il soit surtout populaire\u2026 en Europe&nbsp;!<\/p>\n\n\n\n<p>Le VHDL s\u2019inspire fortement du langage <a href=\"http:\/\/https:\/\/fr.wikipedia.org\/wiki\/Ada_(langage)\">ADA<\/a> pour d\u00e9crire le comportement des circuits int\u00e9gr\u00e9s num\u00e9riques. Cela permet de d\u00e9crire un mod\u00e8le que l\u2019on peut ensuite simuler au moyen d\u2019un simulateur.<br>Le simulateur VHDL OpenSource le plus connu est <a href=\"http:\/\/ghdl.free.fr\/\">GHDL<\/a>, initialement d\u00e9velopp\u00e9 par le fran\u00e7ais <a href=\"https:\/\/linuxfr.org\/news\/entretien-avec-tristan-gingold-auteur-de-ghdl\">Tristan Gringold<\/a> comme une surcouche \u00e0 GCC.<br>Il existe \u00e9galement un simulateur opensource nomm\u00e9 <a href=\"http:\/\/www.fabienm.eu\/flf\/nvc-lautre-simulateur-vhdl-libre\/\">nvc<\/a>, mais il est moins mature que GHDL. Les autres simulateurs OpenSource, comme <a href=\"http:\/\/freehdl.seul.org\/\">FreeHDL<\/a> ou VerilatorVHDL, sont plus anecdotiques.<\/p>\n\n\n\n<p>\u00c0 partir d\u2019une source VHDL il est \u00e9galement possible de g\u00e9n\u00e9rer un sch\u00e9ma de portes et de bascules logiques au moyen d\u2019un logiciel de synth\u00e8se. Pendant longtemps, les seuls logiciels libres de synth\u00e8se HDL ciblaient le Verilog (<a href=\"https:\/\/symbiflow.readthedocs.io\/projects\/vtr\/en\/latest\/odin\/\">OdinII<\/a> et <a href=\"http:\/\/www.clifford.at\/yosys\/\">Yosys<\/a>). Mais depuis <a href=\"https:\/\/github.com\/ghdl\/ghdl-yosys-plugin\">cette ann\u00e9e<\/a> une extension GHDL est apparue pour Yosys. Si cette extension n\u2019est pas encore vraiment stabilis\u00e9e, elle n\u2019en reste pas moins parfaitement utilisable comme nous allons le voir dans cet exemple.<\/p>\n\n\n\n<p>Le VHDL est tr\u00e8s hi\u00e9rarchique, on d\u00e9crit des modules avec leurs entr\u00e9es-sorties que l\u2019on assemble ensuite \u00e0 la mani\u00e8re d\u2019un sch\u00e9ma bloc dans un composant \u00abtop\u00bb.<\/p>\n\n\n\n<p>Dans le cas de TapTempo, l\u2019interface du module \u00abtop\u00bb est d\u00e9clar\u00e9e dans une entit\u00e9 <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/taptempo.vhd\">comme ceci<\/a>&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>entity taptempo is\n    port (\n        clk_i : in std_logic;\n        btn_i : in std_logic;\n        pwm_o : out std_logic\n    );\nend entity taptempo;<\/code><\/pre>\n\n\n\n<p>VHDL n\u2019est pas sensible \u00e0 la casse&nbsp;! C\u2019est quelque chose qui est tr\u00e8s perturbant mais dans l\u2019exemple ci-dessus <code>Entity<\/code> est exactement identique \u00e0 <code>entity<\/code> ou <code>ENTITY<\/code>. En pratique, le simulateur mettra tout en minuscule.<\/p>\n\n\n\n<p>Le module poss\u00e8de deux entr\u00e9es : l\u2019horloge (<code>clk_i<\/code>) et le bouton (<code>btn_i<\/code>) ainsi qu\u2019une sortie pwm (<code>pwm_o<\/code>) pour l\u2019affichage. Il est possible de d\u00e9finir des param\u00e8tres pour la g\u00e9n\u00e9ration du module en utilisant le mot clef <a href=\"https:\/\/www.ics.uci.edu\/%7Ejmoorkan\/vhdlref\/generics.html\"><code>generic<\/code><\/a> de la m\u00eame mani\u00e8re que le <code>port<\/code>. Pour plus de simplicit\u00e9 nous choisirons plut\u00f4t de mettre tous les param\u00e8tres dans un package s\u00e9par\u00e9 (nomm\u00e9 <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/taptempo_pkg.vhd\">taptempo_pkg.vhd<\/a>), \u00e0 la mani\u00e8re des <code>include<\/code> du C\/C++.<\/p>\n\n\n\n<p>Le type <code>std_logic<\/code> est utilis\u00e9 pour repr\u00e9senter les \u00e9tats que peut prendre un signal. En simulation, il peut prendre 9 valeurs diff\u00e9rentes :<br>&#8211; <code>0<\/code> : 0 logique;<br>&#8211; <code>1<\/code> : 1 logique;<br>&#8211; <code>Z<\/code> : Haute imp\u00e9dence;<br>&#8211; <code>X<\/code> : inconnue, plusieurs pilotes en conflit -&gt; erreur;<br>&#8211; <code>L<\/code> : 0 faible, peut-\u00eatre vu comme une r\u00e9sistance de tirage \u00e0 la masse (pull-down);<br>&#8211; <code>H<\/code> : 1 faible, peut-\u00eatre vu comme une r\u00e9sistance de tirage \u00e0 Vcc (pull-up);<br>&#8211; <code>W<\/code> : signal faible inconnu, quand il y a un conflit entre <code>L<\/code> et <code>H<\/code> mais qu\u2019un troisi\u00e8me pilote fort (<code>0<\/code> ou <code>1<\/code>) pourrait tout de m\u00eame mettre le signal \u00e0 une valeur d\u00e9terminable;<br>&#8211; <code>-<\/code> : pas d\u2019importance;<br>&#8211; <code>U<\/code> : non initialis\u00e9.<\/p>\n\n\n\n<p>Dans la pratique on \u00e9vitera d\u2019utiliser toutes ces valeurs si on veut limiter les probl\u00e8mes \u00e0 la synth\u00e8se. Par exemple les valeurs &lsquo;L&rsquo; et &lsquo;H&rsquo; ne peuvent \u00eatre synth\u00e9tis\u00e9es \u00ab&nbsp;\u00e0 l\u2019int\u00e9rieur&nbsp;\u00bb d\u2019un FPGA, les r\u00e9sistances de tirage ne sont disponibles que sur les entr\u00e9es sorties.<br>Un bus de donn\u00e9es bidirectionnel \u00e0 l\u2019ext\u00e9rieur du FPGA devra \u00eatre s\u00e9par\u00e9 en deux bus dans le FPGA&nbsp;: un pour chaque direction.<\/p>\n\n\n\n<p>En r\u00e9alit\u00e9 on se limite \u00e0 &lsquo;0&rsquo; et &lsquo;1&rsquo;. Les valeurs &lsquo;U&rsquo; et &lsquo;X&rsquo; apparaissent dans les traces de simulation et nous avertissent d\u2019un probl\u00e8me avec notre code (simulation ou synth\u00e8se).<\/p>\n\n\n\n<p>Le type <code>std_logic<\/code> peut \u00eatre \u00e9tendu en tableau avec <code>std_logic_vector<\/code> tr\u00e8s pratique pour repr\u00e9senter des bus.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"toc-architecture-de-taptempo\">Architecture de TapTempo<\/h2>\n\n\n\n<p>L\u2019architecture global de TapTempo qui est exactement la m\u00eame que celle de la version Verilog est pr\u00e9sent\u00e9 dans le sch\u00e9ma ci-dessous:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/img.linuxfr.org\/img\/687474703a2f2f66616269656e6d2e65752f706172746167652f736368656d615f74617074656d706f5f637261796f6e2e6a7067\/schema_taptempo_crayon.jpg\" alt=\"schema blocs taptempo\"\/><\/figure>\n\n\n\n<p>Le tempo en entr\u00e9e est donn\u00e9 par une touche de morse<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/img.linuxfr.org\/img\/687474703a2f2f66616269656e6d2e65752f706172746167652f74617074656d706f5f656e747265655f6d6f7273655f6d6f6469662e6a7067\/taptempo_entree_morse_modif.jpg\" alt=\"Sch\u00e9ma de la touche morse\"\/><\/figure>\n\n\n\n<p>qui peut ais\u00e9ment \u00eatre remplac\u00e9 par un simple bouton poussoir (c\u2019est d\u2019ailleurs mont\u00e9 en parall\u00e8le du bouton de la colorlight). Cette entr\u00e9e est synchronis\u00e9e avec l\u2019horloge du FPGA au moyen d\u2019une double bascule en s\u00e9rie pour \u00e9viter la m\u00e9tastabilit\u00e9&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    -- Synchronize btn\n    btn_sync_p: process (clk_i, rst)\n    begin\n        if rst = '1' then\n            btn_s &lt;= '0';\n            btn_old &lt;= '0';\n        elsif rising_edge(clk_i) then\n            btn_s &lt;= btn_old;\n            btn_old &lt;= btn_i;\n        end if;\n    end process btn_sync_p;<\/code><\/pre>\n\n\n\n<p>Le module <code>debounce<\/code> se charge ensuite de filtrer les rebonds et transfert le signal au module de mesure de la p\u00e9riode d\u2019appuis.<\/p>\n\n\n\n<p>Cette p\u00e9riode divise ensuite une constante de temps repr\u00e9sentant le nombre de cycle de <code>timepulse<\/code> pour donner la fr\u00e9quence en <strong>b<\/strong>attement <strong>p<\/strong>ar <strong>m<\/strong>inute (bpm).<\/p>\n\n\n\n<p>Pour \u00eatre \u00ab&nbsp;affich\u00e9e&nbsp;\u00bb, cette valeur est transform\u00e9e en un signal p\u00e9riodique avec un rapport cyclique proportionnel au battement <code>bpm<\/code>.<\/p>\n\n\n\n<p>Ce signal cyclique s\u2019affiche ensuite tr\u00e8s bien au moyen d\u2019un voltm\u00e8tre \u00e0 aiguille comme celui-ci&nbsp;:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/img.linuxfr.org\/img\/687474703a2f2f66616269656e6d2e65752f706172746167652f766f6c746d657472655f74617074656d706f2e6a7067\/voltmetre_taptempo.jpg\" alt=\"Photo du voltm\u00e8tre \u00e0 aiguille\"\/><\/figure>\n\n\n\n<p>La graduation qui nous int\u00e9resse ici est celle qui va de 0 \u00e0 250.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-les-param\u00e8tres-dans-un-package--taptempo_pkg\">Les param\u00e8tres dans un package : <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/taptempo_pkg.vhd\">taptempo_pkg<\/a><\/h3>\n\n\n\n<p>Pour configurer les diff\u00e9rentes constantes et d\u00e9finir des fonctions utiles, on utilise un \u00ab&nbsp;package&nbsp;\u00bb nomm\u00e9 <code>taptempo_pkg.vhd<\/code> qui sera inclus dans tous les modules du projet&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>use work.taptempo_pkg.all;<\/code><\/pre>\n\n\n\n<p>La d\u00e9claration d\u2019un package en VHDL s\u2019effectue en deux temps&nbsp;:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>on d\u00e9clare d\u2019abord les \u00abobjets\u00bb que l\u2019on va utiliser dans le <code>package<\/code>&nbsp;:<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>-- La d\u00e9claration de ce que le package rend visible\npackage taptempo_pkg is\n\n    constant CLK_PER_NS : natural;\n    constant BPM_MAX : natural;\n    constant BPM_SIZE : natural;\n    constant TP_CYCLE : natural;\n    constant BTN_PER_MAX : natural;\n    constant BTN_PER_SIZE : natural;\n    constant BTN_PER_MIN : natural;\n    constant MIN_US : natural;\n    constant MAX_COUNT : natural;\n    constant DEBOUNCE_PER_US: natural;\n    constant DEB_MAX_COUNT : natural;\n    constant DEB_MAX_COUNT_SIZE : natural;\n\n    constant ZEROS : std_logic_vector(31 downto 0);\n\n    -- Usefull function for register size\n    function log2ceil(m : integer) return integer;\n\nend package taptempo_pkg;<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>Puis on d\u00e9finit leurs valeurs et comportement dans le corps (<code>body<\/code>)&nbsp;:<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>package body taptempo_pkg is\n\n    constant CLK_PER_NS : natural := 40;\n    constant BPM_MAX : natural := 250;\n\n    -- period of tp in ns\n    constant TP_CYCLE : natural := 5120;\n\n    -- Debounce period in us\n    constant DEBOUNCE_PER_US: natural := 50_000;\n    constant DEB_MAX_COUNT : natural := (1000 * (DEBOUNCE_PER_US \/ TP_CYCLE));\n    constant DEB_MAX_COUNT_SIZE : natural := log2ceil(DEB_MAX_COUNT);\n\n    -- constant MIN_NS : natural := 60000000000;\n    constant MIN_US : natural := 60_000_000;\n\n    constant BPM_SIZE : natural := log2ceil(BPM_MAX + 1);\n\n    constant BTN_PER_MAX : natural := 1000 * (MIN_US \/ TP_CYCLE);\n    constant BTN_PER_SIZE : natural := log2ceil(BTN_PER_MAX + 1);\n    constant BTN_PER_MIN : natural := 1000 * (MIN_US \/ TP_CYCLE) \/ BPM_MAX;\n    constant MAX_COUNT : natural := TP_CYCLE \/ CLK_PER_NS;\n\n    constant ZEROS : std_logic_vector(31 downto 0) := x\"00000000\";\n\n    function log2ceil(m : integer) return integer is\n    begin\n      for i in 0 to integer'high loop\n            if 2 ** i >= m then\n                return i;\n            end if;\n        end loop;\n    end function log2ceil;\n\nend package body;<\/code><\/pre>\n\n\n\n<p>Vous noterez la lourdeur d\u2019avoir \u00e0 d\u00e9clarer le type de la constante dans le package avant de donner sa valeur dans le <code>body<\/code>. Les mauvaises langues diront que \u00e7a n\u2019est pas beaucoup plus lourd que le C++ et ses doubles fichier (*.h et *.cpp) pour d\u00e9finir une classe.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-un-cadenceur-timer-pour-tout-le-syst\u00e8me--timepulse\">Un cadenceur (timer) pour tout le syst\u00e8me : <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/timepulse.vhd\">Timepulse<\/a><\/h3>\n\n\n\n<p>Nous allons avoir besoin de compter le temps dans plusieurs blocs du projet. L\u2019horloge c\u00e2bl\u00e9e sur la colorlight est de 25Mhz. Comme nous n\u2019avons pas besoin de pr\u00e9cisions \u00e0 la quarantaine de nanoseconde nous allons utiliser un compteur global pour g\u00e9n\u00e9rer des \u00ab&nbsp;pulses&nbsp;\u00bb toutes les 5,12\u00b5s. Ces pulses seront utilis\u00e9s en entr\u00e9e des blocs ayant besoin de compter le temps. Cela r\u00e9duira la taille des compteurs puisqu\u2019ils n\u2019auront pas \u00e0 repartir de l\u2019horloge globale.<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/timepulse.vhd\">Le code<\/a> est suffisamment concis pour que nous puissions le reproduire ici dans son int\u00e9gralit\u00e9.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>library ieee;\nuse ieee.std_logic_1164.all;\nuse ieee.numeric_std.all;\n\nuse work.taptempo_pkg.all;\n\nentity timepulse is\n    port (\n        -- clock and reset\n        clk_i : in std_logic;\n        rst_i : in std_logic;\n        -- timepulse output\n        tp_o : out std_logic\n    );\nend entity timepulse;\n\narchitecture RTL of timepulse is\n    signal counter : natural range 0 to MAX_COUNT + 1;\nbegin\n    tp_o &lt;= '1' when (counter = 0) else '0';\n\n    counter_p: process (clk_i, rst_i)\n    begin\n        if rst_i = '1' then\n            counter &lt;= 0;\n        elsif rising_edge(clk_i) then\n            if counter &lt; MAX_COUNT then\n                counter &lt;= counter + 1;\n            else\n                counter &lt;= 0;\n            end if;\n        end if;\n    end process counter_p;\nend architecture RTL;<\/code><\/pre>\n\n\n\n<p>Tout module VHDL est constitu\u00e9 d\u2019une partie entit\u00e9 <code>entity<\/code> pour d\u00e9crire les interfaces d\u2019entr\u00e9es-sorties. Ici nous avons les deux entr\u00e9es horloge <code>clk_i<\/code> et reset <code>rst_i<\/code> caract\u00e9ristiques d\u2019un syst\u00e8me synchrone. L\u2019unique sortie est le pulse g\u00e9n\u00e9r\u00e9 toutes les 5,12\u00b5s.<\/p>\n\n\n\n<p>La description du fonctionnement du module est donn\u00e9e ensuite dans le bloc \u00ab&nbsp;<em>architecture<\/em>&nbsp;\u00bb. En plus des signaux d\u2019entr\u00e9es-sorties, il est possible de d\u00e9clarer des signaux interne au bloc. Nous avons d\u00e9clar\u00e9 ici le signal <code>counter<\/code> qui est un entier naturel born\u00e9 de 0 \u00e0 <code>MAX_COUNT + 1<\/code>. La possibilit\u00e9 de borner les signaux que l\u2019on d\u00e9clare en <em>integer<\/em> permet d\u2019aider l\u2019outil de synth\u00e8se \u00e0 g\u00e9n\u00e9rer un bus de taille adapt\u00e9e quand on ne souhaite pas d\u00e9clarer un <code>std_logic_vector<\/code> avec une taille explicite.<\/p>\n\n\n\n<p>Attention, un <code>signal<\/code> n\u2019a pas la m\u00eame signification qu\u2019une variable dans un programme proc\u00e9dural. Une variable prend une seule valeur qui changera au cours de l\u2019ex\u00e9cution du programme. Un signal doit \u00eatre vu comme une liste des diff\u00e9rentes valeurs que prendra le signal au cours de la simulation. La premi\u00e8re valeur de la liste sera son \u00e9tat initial, et \u00e0 chaque \u00e9v\u00e9nement sur le signal on ajoutera une valeur \u00e0 la liste jusqu\u2019\u00e0 la fin de la simulation.<\/p>\n\n\n\n<p>Nous aurions \u00e9galement pu d\u00e9clarer des constantes \u00e0 cet endroit, mais comme dit dans l\u2019introduction, une constante \u00e9tant un param\u00e8tre modifiable nous avons pr\u00e9f\u00e9r\u00e9 le mettre dans le package <code>taptempo_pkg<\/code>.<\/p>\n\n\n\n<p>Le corps de l\u2019architecture de timepulse poss\u00e8de deux \u00ab&nbsp;process&nbsp;\u00bb concurrents qui s\u2019ex\u00e9cutent en parall\u00e8le. Le premier est une assignation continue&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    tp_o &lt;= '1' when (counter = 0) else '0';<\/code><\/pre>\n\n\n\n<p>Il fait passer <code>tp_o<\/code> \u00e0 &lsquo;1&rsquo; quand le compteur <code>counter<\/code> atteint la valeur 0. Ce \u00ab&nbsp;process&nbsp;\u00bb est activ\u00e9 \u00e0 chaque \u00e9venement\/changement sur le signal <code>counter<\/code>.<\/p>\n\n\n\n<p>Le second process est plus \u00e9toff\u00e9 :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    counter_p: process (clk_i, rst_i)\n    begin\n    --...\n    end process counter_p;<\/code><\/pre>\n\n\n\n<p>Ce process est ex\u00e9cut\u00e9 \u00e0 chaque \u00e9v\u00e9nement surgissant sur les signaux d\u00e9clar\u00e9s dans la liste de sensibilit\u00e9 (ici <code>clk_i<\/code> et <code>rst_i<\/code>). Le contenu du <code>process<\/code> est quant \u00e0 lui ex\u00e9cut\u00e9 de mani\u00e8re s\u00e9quentielle.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>        if rst_i = '1' then\n            --...\n        elsif rising_edge(clk_i) then\n            --...\n        end if;<\/code><\/pre>\n\n\n\n<p>Cette structure d\u00e9crit une <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Bascule_(circuit_logique)#Bascule_D\">bascule D<\/a> qui sera bien reconnue comme telle par le logiciel de synth\u00e8se.<br>Un reset asynchrone r\u00e9-initialise le compteur \u00e0 0:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>            counter &lt;= 0;<\/code><\/pre>\n\n\n\n<p>Et sur un front montant de l\u2019horloge<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>        elsif rising_edge(clk_i) then<\/code><\/pre>\n\n\n\n<p>on incr\u00e9mente le compteur jusqu\u2019\u00e0 son maximum.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>            if counter &lt; MAX_COUNT then\n                counter &lt;= counter + 1;\n            else\n                counter &lt;= 0;\n            end if;<\/code><\/pre>\n\n\n\n<p>Notez l\u2019utilisation de l\u2019op\u00e9rateur d\u2019affectation non bloquant <code>&lt;=<\/code> qui n\u2019affectera la valeur qu\u2019\u00e0 la fin de l\u2019ex\u00e9cution du process.<\/p>\n\n\n\n<p>Si par exemple nous avons le code suivant dans un process&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    counter &lt;= 10;\n    counter &lt;= 5;<\/code><\/pre>\n\n\n\n<p>La valeur effective de <code>counter<\/code> \u00e0 la fin de l\u2019ex\u00e9cution de process sera 5. Et \u00e0 aucun moment la valeur 10 n\u2019appara\u00eetra dans <code>counter<\/code>.<\/p>\n\n\n\n<p>Pour visualiser les signaux de sortie du module on pourra se rendre dans <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/tree\/master\/cocotb\/test_timepulse\">le r\u00e9pertoire<\/a> <code>cocotb\/test_timepulse<\/code> du projet et lancer le test en donnant ghdl comme simulateur:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ cd test_timepulse\n$ SIM=ghdl make<\/code><\/pre>\n\n\n\n<p>Le m\u00eame principe pourra \u00eatre appliqu\u00e9 pour simuler tous les autres modules de TapTempo.<\/p>\n\n\n\n<p>La simulation consiste \u00e0 laisser passer le temps pendant une miliseconde :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@cocotb.test()\nasync def double_push_test(dut):\n    trg = TestTimePulse(dut)\n    trg.display_config()\n    trg.log.info(\"Running test!\")\n    await trg.reset()\n    await Timer(1, units=\"ms\")<\/code><\/pre>\n\n\n\n<p>La simulation g\u00e9n\u00e8re un fichier nomm\u00e9 <code>timepulse.fst<\/code> visible avec <a href=\"https:\/\/github.com\/gtkwave\">gtkwave<\/a> :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ gtkwave timepulse.fst<\/code><\/pre>\n\n\n\n<p>Comme visible dans l\u2019image ci-dessous.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/img.linuxfr.org\/img\/687474703a2f2f7777772e66616269656e6d2e65752f6c696e757866722f74696d6570756c73655f776176652e6a7067\/timepulse_wave.jpg\" alt=\"Visualisation de la simulation timepulse avec gtkwave\" title=\"Source : http:\/\/www.fabienm.eu\/linuxfr\/timepulse_wave.jpg\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-filtrage-des-rebonds--debounce\">Filtrage des rebonds : <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/debounce.vhd\">debounce<\/a><\/h3>\n\n\n\n<p>Le manipulateur morse g\u00e9n\u00e8re des rebonds, le filtrage avait initialement \u00e9t\u00e9 r\u00e9gl\u00e9 \u00e0 20&nbsp;ms sur la version Verilog. Pour \u00eatre vraiment tranquille nous le monterons \u00e0 50&nbsp;ms, mais c\u2019est particuli\u00e8rement long comme p\u00e9riode. Comme pour le reste, cette configuration se trouve donc dans le package <code>taptempo_pkg.vhd<\/code>.<\/p>\n\n\n\n<p>Le module <code>debounce<\/code> va se charger de filtrer les rebonds au moyen d\u2019une machine \u00e0 \u00e9tats et d\u2019un compteur.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>entity debounce is\n    port (\n        -- clock and reset\n        clk_i : in std_logic;\n        rst_i : in std_logic;\n        -- inputs\n        tp_i  : in std_logic;\n        btn_i : in std_logic;\n        -- outputs\n        btn_o : out std_logic\n    );\nend entity;<\/code><\/pre>\n\n\n\n<p>Le comptage des pulsations du module timepulse s\u2019effectue dans un process :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>counter_p: process (clk_i, rst_i)\nbegin\n    if rst_i = '1' then\n        counter &lt;= 0;\n    elsif rising_edge(clk_i) then\n        if tp_i = '1' then\n            if (state_reg = s_cnt_high) or (state_reg = s_cnt_low) then\n                counter &lt;= counter + 1;\n            else\n                counter &lt;= 0;\n            end if;\n        end if;\n    end if;\nend process counter_p;<\/code><\/pre>\n\n\n\n<p>La machine \u00e0 \u00e9tats est constitu\u00e9e de 4 \u00e9tats&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    type t_state is (s_wait_low, s_wait_high, s_cnt_high, s_cnt_low);\n    signal state_reg : t_state;<\/code><\/pre>\n\n\n\n<p>Deux \u00e9tats d\u2019attentes et deux \u00e9tats de comptages. Dans un \u00e9tat d\u2019attente <code>s_wait_<\/code> on attend un front du bouton. Quand un front survient on passe dans un \u00e9tat de comptage <code>s_cnt_<\/code>.<\/p>\n\n\n\n<p>Dans un \u00e9tat de comptage, le compteur s\u2019incr\u00e9mente et passe \u00e0 l\u2019\u00e9tat d\u2019attente lorsqu\u2019il arrive \u00e0 son maximum.<\/p>\n\n\n\n<p>De cette mani\u00e8re, seuls les fronts intervenant dans un \u00e9tat d\u2019attente sont pris en compte. Une fois la machine \u00e0&nbsp;\u00e9tats dans un \u00e9tat de comptage, plus aucun front du bouton n\u2019est \u00ab&nbsp;\u00e9cout\u00e9&nbsp;\u00bb.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-mesure-de-la-p\u00e9riode-dappuis--percount\">Mesure de la p\u00e9riode d\u2019appuis : <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/percount.vhd\">percount<\/a><\/h3>\n\n\n\n<p>Le module <code>percount<\/code> (<code>per<\/code>iod <code>count<\/code>er) va compter le temps d\u2019une p\u00e9riode entre deux appuis sur le bouton.<\/p>\n\n\n\n<p>Si on passe sur les signaux d\u00e9sormais bien connus <code>clk_i<\/code>, <code>rst_i<\/code> et <code>tp_i<\/code>; l\u2019entr\u00e9e du module est <code>btn_i<\/code> qui repr\u00e9sente la valeur du bouton (appuy\u00e9 ou non) sans rebond.<\/p>\n\n\n\n<p>Les deux signaux de sortie sont :<br>&#8211; Un vecteur <code>btn_per_o<\/code> repr\u00e9sentant la valeur de la p\u00e9riode mesur\u00e9e<br>&#8211; Un signal <code>btn_per_valid<\/code> donnant la validit\u00e9 de la valeur pr\u00e9c\u00e9dente. La valeur de <code>btn_per_o<\/code> est consid\u00e9r\u00e9e comme bonne uniquement si <code>btn_per_valid<\/code> est \u00e0 &lsquo;1&rsquo;.<\/p>\n\n\n\n<p>Dans un premier <code>process<\/code> d\u00e9tecte les fronts descendants du bouton :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    btn_fall &lt;= btn_old and (not btn_i);\n\n    btn_p: process (clk_i, rst_i)\n    begin\n        if rst_i = '1' then\n            btn_old &lt;= '0';\n        elsif rising_edge(clk_i) then\n            btn_old &lt;= btn_i;\n        end if;\n    end process btn_p;<\/code><\/pre>\n\n\n\n<p>Puis dans un second process on compte le temps et on remet \u00e0 0 le compteur quand un front descendant du bouton surgit :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>        --...\n        elsif rising_edge(clk_i) then\n            if btn_fall = '1' then\n                counter_valid &lt;= '1';\n            elsif counter_valid = '1' then\n                counter &lt;= 0;\n                counter_valid &lt;= '0';\n            elsif (tp_i = '1') and (counter &lt; BTN_PER_MAX) then\n                counter &lt;= counter + 1;\n            end if;\n        end if;\n        -- ...<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-conversion-p\u00e9riodefr\u00e9quence-division--per2bpm\">Conversion p\u00e9riode\/fr\u00e9quence (division) : <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/per2bpm.vhd\">per2bpm<\/a><\/h3>\n\n\n\n<p>C\u2019est la partie \u00ab&nbsp;dure&nbsp;\u00bb du syst\u00e8me, il va falloir faire une division d\u2019une constante par la p\u00e9riode mesur\u00e9e.<\/p>\n\n\n\n<p>Pour \u00e9viter d\u2019avoir \u00e0 utiliser les blocs multiplieurs d\u00e9di\u00e9s de l\u2019ECP5 et rester \u00ab&nbsp;portable&nbsp;\u00bb nous allons poser cette division comme on le ferait \u00e0 la main.<\/p>\n\n\n\n<p>Pour ce faire, on va mettre le dividende dans un registre nomm\u00e9 <code>remainder<\/code> (pour le reste) :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>remainder &lt;= std_logic_vector(to_unsigned((MIN_US \/ TP_CYCLE) * 1000, remainder'length));<\/code><\/pre>\n\n\n\n<p>Et le diviseur dans un registre <code>divisor<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if to_integer(unsigned(btn_per_i)) &lt; BTN_PER_MIN then\n    divisor &lt;= std_logic_vector(to_unsigned(BTN_PER_MIN, BTN_PER_SIZE)) &amp; ZEROS(DIVIDENTWIDTH-1 downto 0);\nelse\n    divisor &lt;= btn_per_i &amp; ZEROS(DIVIDENTWIDTH-1 downto 0);\nend if;<\/code><\/pre>\n\n\n\n<p>Notez la la valeur minimum de <code>btn_per_i<\/code> qui nous \u00e9vitera les probl\u00e8mes de division par 0 ainsi que des valeurs de bpm trop \u00e9lev\u00e9es.<\/p>\n\n\n\n<p>La division est cadenc\u00e9e par une machine \u00e0 \u00e9tats de 3&nbsp;\u00e9tats :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>type t_state is (s_init, s_compute, s_result);\nsignal state_reg : t_state;<\/code><\/pre>\n\n\n\n<p>Un \u00e9tat initial pour d\u00e9marrer, un \u00e9tat de calcul et un \u00e9tat durant lequel la valeur de sortie est consid\u00e9r\u00e9e comme bonne.<\/p>\n\n\n\n<p>L\u2019algorithme de division consiste en une s\u00e9rie de d\u00e9calage \u00e0 droite du diviseur :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>divisor &lt;= \"0\" &amp; divisor(REGWIDTH-1 downto 1);<\/code><\/pre>\n\n\n\n<p>Et de d\u00e9calages \u00e0 gauche du quotient :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if (unsigned(divisor) &lt;= unsigned(remainder)) then\n    remainder &lt;= std_logic_vector(unsigned(remainder) - unsigned(divisor));\n    quotient &lt;= quotient(REGWIDTH-2 downto 0) &amp; \"1\";\nelse\n    quotient &lt;= quotient(REGWIDTH-2 downto 0) &amp; \"0\";\nend if;<\/code><\/pre>\n\n\n\n<p>Avec ajout d\u2019un &lsquo;1&rsquo; ou d\u2019un &lsquo;0&rsquo; en fonction de la valeur du diviseur et du reste. Lorsque le reste est sup\u00e9rieur au diviseur, on le soustrait du diviseur et on ajoute un bit &lsquo;1&rsquo; au moment du d\u00e9calage du quotient. Sinon on ajoute le bit &lsquo;0&rsquo; sans toucher au registre de reste (<code>remainder<\/code>).<\/p>\n\n\n\n<p>Les registres <code>remainder<\/code>, <code>divisor<\/code> et <code>quotient<\/code> sont des <code>std_logic_vector<\/code> qui ne repr\u00e9sente rien d\u2019autre qu\u2019un \u00ab&nbsp;paquet de bits&nbsp;\u00bb pour le langage. Si l\u2019on souhaite effectuer des op\u00e9rations de comparaison ou des additions soustraction dessus il faut donc dire au VHDL que ses bits repr\u00e9sentent un nombre (ici non sign\u00e9) en les \u00ab&nbsp;castant&nbsp;\u00bb avec <code>unsigned()<\/code>. Et si l\u2019on souhaite assigner le r\u00e9sultat \u00e0 un <code>std_logic_vector<\/code> il faut \u00ab&nbsp;caster&nbsp;\u00bb \u00e0 nouveau avec <code>std_logic_vector<\/code>. Ces multiples conversion de type peuvent vite devenir un casse-t\u00eate quand on fait du VHDL.<\/p>\n\n\n\n<p>L\u2019esperluette <code>&amp;<\/code> est ici un op\u00e9rateur de concat\u00e9nation de deux vecteurs <code>std_logic_vector<\/code>.<\/p>\n\n\n\n<p>Le r\u00e9sultat de la division est donn\u00e9 en continu par les bits de poids faible du registre <code>divisor<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>bpm_o &lt;= quotient(BPM_SIZE-1 downto 0);\nbpm_valid &lt;= '1' when state_reg = s_result else '0';<\/code><\/pre>\n\n\n\n<p><code>bpm_o<\/code> est consid\u00e9r\u00e9 comme valide lorsque l\u2019\u00e9tat de la machine \u00e0 \u00e9tats est <code>s_result<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-sortie-affichage-en-rapport-cyclique--pwmgen\">Sortie \u00abaffichage\u00bb en rapport cyclique : <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/vhdl\/pwmgen.vhd\">pwmgen<\/a><\/h3>\n\n\n\n<p>La sortie&nbsp;\u00ab&nbsp;d\u2019affichage&nbsp;\u00bb de la valeur consiste \u00e0 g\u00e9n\u00e9rer un signal p\u00e9riodique <code>pwm_o<\/code> dont le rapport cyclique est proportionnel \u00e0 la valeur de <code>bpm_o<\/code>.<\/p>\n\n\n\n<p>On va pour cela compter de 0 \u00e0 BPM_MAX :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- count\ncount_p: process (clk_i, rst_i)\nbegin\n    if rst_i = '1' then\n        count &lt;= BPM_MAX;\n    elsif rising_edge(clk_i) then\n        if tp_i = '1' then\n            if count = 0 then\n                count &lt;= BPM_MAX;\n            else\n                count &lt;= count - 1;\n            end if ;\n        end if ;\n    end if;\nend process count_p;<\/code><\/pre>\n\n\n\n<p>Tant que la valeur du compteur se trouvera en dessous d\u2019un seuil <code>pwmthreshold<\/code> elle sera \u00e0 &lsquo;1&rsquo;, une fois le seuil d\u00e9pass\u00e9 elle passera \u00e0 &lsquo;0&rsquo;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- pwm output\npwm_o &lt;= '1' when (count &lt; pwmthreshold) else '0';<\/code><\/pre>\n\n\n\n<p>Ce seuil ne doit pas changer de valeur pendant le comptage, on va donc devoir utiliser un registre&nbsp;interm\u00e9diaire <code>bpm_reg<\/code> pour stocker la valeur de <code>bpm_i<\/code> d\u2019entr\u00e9e&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- Latching bpm_i on bpm_valid\nbpm_register_p: process (clk_i, rst_i)\nbegin\n    if rst_i = '1' then\n        bpm_reg &lt;= BPM_RESET;\n        pwmthreshold &lt;= BPM_RESET;\n    elsif rising_edge(clk_i) then\n        if bpm_valid = '1' then\n            bpm_reg &lt;= to_integer(unsigned(bpm_i));\n        end if;\n        if (count = BPM_MAX) then\n            pwmthreshold &lt;= bpm_reg;\n        end if;\n    end if;\nend process bpm_register_p;<\/code><\/pre>\n\n\n\n<p>Notez, \u00e0 nouveau, la conversion d\u2019un <code>std_logic_vector<\/code> (<code>bpm_i<\/code>) vers un entier naturel (<code>bpm_reg<\/code>) par le double \u00ab&nbsp;cast&nbsp;\u00bb <code>to_integer(unsigned())<\/code><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"toc-synth\u00e8se-placement-routage-et-configuration\">Synth\u00e8se, placement-routage et configuration<\/h2>\n\n\n\n<p>Nous voici arriv\u00e9 \u00e0 la v\u00e9ritable information de cette d\u00e9p\u00eache : <strong>il est d\u00e9sormais possible de faire de la synth\u00e8se VHDL avec un logiciel libre<\/strong>.<br>Le VHDL est le langage avec lequel les \u00e9tudiants fran\u00e7ais abordent le monde du FPGA en g\u00e9n\u00e9ral. Je l\u2019ai personnellement beaucoup pratiqu\u00e9 en utilisant ghdl pour la simulation et gtkwave pour visualiser les chronogrammes. Mais jusqu\u2019\u00e0 cette ann\u00e9e, j\u2019ai toujours d\u00fb passer sur les monstres propri\u00e9taires fournis gratuitement par les fabricants de FPGA pour la partie synth\u00e8se. Monstres de plusieurs dizaines de Giga-octets \u00e0 t\u00e9l\u00e9charger, souvent pr\u00e9compil\u00e9 pour une architecture 32 ou 64 bits mais pas les deux, incluant des machines virtuelles java et autres librairies graphique bugu\u00e9s quand on change la langue du syst\u00e8me (coucou le <a href=\"https:\/\/forums.xilinx.com\/t5\/Memory-Interfaces-and-NoC\/MIG-missing-input-clock-option\/m-p\/746423\/highlight\/true#M10242\">MIG de Vivado<\/a>). Quand ils ne sont tout simplement pas compatibles Linux (de plus en plus rare cependant). Bref, la synth\u00e8se VHDL n\u2019\u00e9tait pas une sin\u00e9cure.<\/p>\n\n\n\n<p>La simulation n\u2019\u00e9tait pas en reste non plus, car si GHDL fonctionnait plut\u00f4t bien, il n\u2019\u00e9tait pas des plus rapides \u00e0 l\u2019\u00e9poque. Et impossible de faire de la simulation mixte en m\u00e9langeant du VHDL et du Verilog.<\/p>\n\n\n\n<p>Impossible \u00e9galement d\u2019utiliser la formule 1 de la simulation qu\u2019est <a href=\"https:\/\/www.veripool.org\/wiki\/verilator\">Verilator<\/a>, un simulateur un peu sp\u00e9cial qui converti son design en un objet C++, le testbench \u00e9tant ensuite \u00e9crit comme du C++ \u00abnormal\u00bb. Le rapport de performance est d\u2019au moins <a href=\"http:\/\/www.fabienm.eu\/flf\/icarus-vs-verilator\/\">100 fois plus rapide<\/a>.<\/p>\n\n\n\n<p>Pour \u00eatre honn\u00eate, signalons qu\u2019il existe bien le logiciel fran\u00e7ais <a href=\"http:\/\/coriolis.lip6.fr\/\">Alliance<\/a>, mais c\u2019est tr\u00e8s orient\u00e9 <a href=\"https:\/\/fr.wikipedia.org\/wiki\/Application-specific_integrated_circuit\">ASIC<\/a>, et je n\u2019ai jamais r\u00e9ussi \u00e0 le faire fonctionner correctement. Peut-\u00eatre que si des d\u00e9veloppeurs d\u2019Alliance tra\u00eenent sur LinuxFR, ils pourront nous en parler.<\/p>\n\n\n\n<p>Bref, la lumi\u00e8re est arriv\u00e9e cette ann\u00e9e avec le d\u00e9veloppement du greffon <a href=\"https:\/\/github.com\/ghdl\/ghdl-yosys-plugin\">ghdl pour yosys<\/a> ainsi que les avanc\u00e9es \u00e0 toute vapeur de la partie \u00absynth\u00e8se\u00bb de <a href=\"https:\/\/github.com\/ghdl\/ghdl\">ghdl<\/a>. Comme nous allons le voir dans la section suivante.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-ghdl--yosys-la-lune-de-miel\">GHDL + Yosys, la lune de miel<\/h3>\n\n\n\n<p>Pour pouvoir synth\u00e9tiser TapTempo il va falloir compiler et installer les trois projet suivant&nbsp;:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>GHDL&nbsp;: Le logiciel de simulation ainsi que de synth\u00e8se (ne pas oublier d\u2019option de synth\u00e8se dans la configuration avant compilation)&nbsp;;<\/li><li>Yosys&nbsp;:&nbsp;le logiciel de synth\u00e8se Verilog, v\u00e9ritable couteau suisse pour le FPGA et plus si affinit\u00e9&nbsp;;<\/li><li>ghdl-yosys-plugin : le greffon qui permet de compiler une librairie <code>ghdl.so<\/code> \u00e0 copier dans le r\u00e9pertoire <code>YOSYS_PREFIX\/share\/yosys\/plugins\/<\/code> de yosys.<\/li><\/ul>\n\n\n\n<p>Il est fortement conseill\u00e9 de prendre les derni\u00e8res versions du code des projets ci-dessus et de les compiler \u00ab&nbsp;\u00e0 la main&nbsp;\u00bb car ces projets avancent vite et bougent beaucoup, les paquets des diff\u00e9rentes distributions linux sont d\u00e9j\u00e0 largement obsol\u00e8tes.<\/p>\n\n\n\n<p>Une fois install\u00e9 on peut lancer yosys avec le greffon ghdl comme ceci :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ yosys -m ghdl\n\n&#91;...]\n\n Yosys 0.9+3686 (git sha1 bc085761, clang 8.0.0-svn345496-1~exp1+0~20181029105533.852~1.gbpf10f36 -fPIC -Os)\n\nyosys><\/code><\/pre>\n\n\n\n<p>Puis charger nos sources VHDL avec la commande ghdl, en pr\u00e9cisant bien le nom du \u00ab&nbsp;top&nbsp;\u00bb que l\u2019on souhaite \u00e9laborer avec l\u2019option <code>-e<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>yosys> ghdl --std=08 debounce.vhd  per2bpm.vhd  percount.vhd  pwmgen.vhd  rstgen.vhd  taptempo_pkg.vhd  taptempo.vhd  timepulse.vhd -e taptempo\n1. Executing GHDL.\nImporting module taptempo.\nImporting module rstgen.\nImporting module timepulse.\nImporting module debounce.\nImporting module percount.\nImporting module per2bpm.\nImporting module pwmgen_250.<\/code><\/pre>\n\n\n\n<p>Et c\u2019est tout&nbsp;! Maintenant on peut reprendre les commandes et la proc\u00e9dure que l\u2019on avait utilis\u00e9e dans le cas de <a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-synth%C3%A8se-sur-colorlight\">TapTempo en Verilog pour faire la synth\u00e8se<\/a>, le placement routage et le chargement.<\/p>\n\n\n\n<p>Notez que comme les sources ont \u00e9t\u00e9 charg\u00e9es et pars\u00e9 dans yosys, il est parfaitement possible de le convertir en Verilog gr\u00e2ce \u00e0 la commande <code>write_verilog<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>yosys> write_verilog taptempo_converted.v\n\n2. Executing Verilog backend.\nDumping module `\\debounce'.\nDumping module `\\per2bpm'.\nDumping module `\\percount'.\nDumping module `\\pwmgen_250'.\nDumping module `\\rstgen'.\nDumping module `\\taptempo'.\nDumping module `\\timepulse'.\n<\/code><\/pre>\n\n\n\n<p>La version Verilog ainsi g\u00e9n\u00e9r\u00e9e pourra \u00eatre simul\u00e9e avec verilator ou icarus par exemple pour faire de la simulation mixte, ou tout simplement pour acc\u00e9l\u00e9rer la simulation dans le cas de verilator.<\/p>\n\n\n\n<p>Pour synth\u00e9tiser sur la colorlight qui est munie d\u2019un FPGA ECP5 de chez Lattice, on utilisera la commande synth_ecp5 avec une sortie netlist au format json.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>yosys>  synth_ecp5 -json taptempo.json\n&#91;...]\n=== taptempo ===\n\n   Number of wires:                540\n   Number of wire bits:           1515\n   Number of public wires:         540\n   Number of public wire bits:    1515\n   Number of memories:               0\n   Number of memory bits:            0\n   Number of processes:              0\n   Number of cells:                785\n     CCU2C                         109\n     L6MUX21                         9\n     LUT4                          452\n     PFUMX                          25\n     TRELLIS_FF                    190\n\n2.50. Executing CHECK pass (checking for obvious problems).\nChecking module taptempo...\nFound and reported 0 problems.\n\n2.51. Executing JSON backend.\n<\/code><\/pre>\n\n\n\n<p>Arriv\u00e9 \u00e0 cette \u00e9tape il peut \u00eatre int\u00e9ressant de comparer avec la version Verilog les ressources utilis\u00e9es par le m\u00eame projet&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ yosys\nyosys> read_verilog debounce.v  per2bpm.v  percount.v  pwmgen.v  rstgen.v  taptempo.v  timepulse.v\nyosys> synth_ecp5 -json taptempo.json\n...\n=== taptempo ===\n\n   Number of wires:                487\n   Number of wire bits:           1435\n   Number of public wires:         487\n   Number of public wire bits:    1435\n   Number of memories:               0\n   Number of memory bits:            0\n   Number of processes:              0\n   Number of cells:                751\n     CCU2C                         105\n     L6MUX21                         1\n     LUT4                          441\n     PFUMX                          13\n     TRELLIS_FF                    191\n\n8.50. Executing CHECK pass (checking for obvious problems).\nChecking module taptempo...\nFound and reported 0 problems.\n\n8.51. Executing JSON backend.\n<\/code><\/pre>\n\n\n\n<p>Nous obtenons un design l\u00e9g\u00e8rement plus petit avec la version Verilog, mais les ordres de grandeurs sont tout de m\u00eame respect\u00e9.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-placement-routage-avec-nextpnr\">Placement routage avec NextPnR<\/h3>\n\n\n\n<p><a href=\"https:\/\/github.com\/YosysHQ\/nextpnr\">NextPnR<\/a> est un logiciel de placement routage qui prend le sch\u00e9ma (<a href=\"https:\/\/fr.wikipedia.org\/wiki\/Netlist\">netlist<\/a>) de cellules \u00abprimitives\u00bb g\u00e9n\u00e9r\u00e9 par le logiciel de synth\u00e8se et associe chaque cellule \u00e0 une cellule disponible dans le FPGA. NextPnR effectue \u00e9galement le routage qui consiste \u00e0 \u00e9tablir les connexions entre les diff\u00e9rentes cellules.<\/p>\n\n\n\n<p>C\u2019est \u00e9galement \u00e0 cette \u00e9tape que l\u2019on va pr\u00e9ciser la configuration du FPGA (taille, IO\u2026). En plus du fichier <em>json<\/em> de synth\u00e8se nous donnerons un second fichier de configuration des IO nomm\u00e9 <code>taptempo.lpf<\/code> d\u00e9crivant nos trois signaux d\u2019entr\u00e9es-sortie :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>LOCATE COMP \"clk_i\" SITE \"P6\";\nIOBUF PORT \"clk_i\" IO_TYPE=LVCMOS33;\nFREQUENCY PORT \"clk_i\" 25 MHZ;\n\nLOCATE COMP \"btn_i\" SITE \"M13\";\nIOBUF PORT \"btn_i\" IO_TYPE=LVCMOS33;\n\nLOCATE COMP \"pwm_o\" SITE \"P4\";\nIOBUF PORT \"pwm_o\" IO_TYPE=LVCMOS33;\n<\/code><\/pre>\n\n\n\n<p>Toutes les commandes de synth\u00e8se donn\u00e9es ici sont bien \u00e9videmment disponibles dans un <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/synthesis\/colorlight_vhdl\/Makefile\">Makefile<\/a> sur le d\u00e9pot. Pour faire le placement routage nous pourrions taper la commande suivante :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ nextpnr-ecp5 --25k --package CABGA256 --speed 6 --json taptempo.json --textcfg taptempo_out.config --lpf taptempo.lpf --freq 25\n<\/code><\/pre>\n\n\n\n<p>Qui ne donnera <strong>pas<\/strong> le fichier de configuration nomm\u00e9 <strong>bitstream<\/strong> permettant de configurer le FPGA&nbsp;!<\/p>\n\n\n\n<p>Car les sp\u00e9cifications des FPGA&nbsp;sont gard\u00e9es jalousement par les constructeurs, et il faut des heures et des heures d\u2019ing\u00e9nieries inverses pour venir \u00e0 bout de ces informations. Travail qui a \u00e9t\u00e9 effectu\u00e9 via le projet <a href=\"https:\/\/github.com\/SymbiFlow\/prjtrellis\">Trellis<\/a> et qui nous permet de convertir la sortie texte pr\u00e9c\u00e9dente <code>taptempo_out.config<\/code> en un bitstream reconnu par l\u2019EPC5&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ ecppack taptempo_out.config taptempo.bit\n<\/code><\/pre>\n\n\n\n<p>Et l\u2019on d\u00e9croche enfin le Saint Gr\u00e2\u00e2l permettant de configurer la colorlight : le bitstream <code>taptempo.bit<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-en-avant-la-musique-avec-openfpgaloader\">En avant la musique avec openFPGALoader<\/h3>\n\n\n\n<p>Arriv\u00e9 \u00e0 cette \u00e9tape il serait vraiment dommage d\u2019\u00eatre contraint de relancer l\u2019ide proprio du constructeur juste pour t\u00e9l\u00e9charger le bitstream dans le FPGA via une sonde USB-Jtag&nbsp;!<\/p>\n\n\n\n<p>C\u2019est l\u00e0 que l\u2019on peut d\u00e9gainer le projet <a href=\"https:\/\/github.com\/trabucayre\/openFPGALoader\">openFPGALoader<\/a> qui a pour ambition de permettre la configuration de tous les FPGA&nbsp;existant avec toutes les sondes disponibles sur le march\u00e9.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ openFPGALoader taptempo.bit \nOpen file taptempo.bit DONE\nParse file DONE\nEnable configuration: DONE\nSRAM erase: DONE\nLoading: &#91;==================================================] 100.000000%\nDone\nDisable configuration: DONE\n<\/code><\/pre>\n\n\n\n<p>Et voila, on peut maintenant taper taper taper <a href=\"https:\/\/www.youtube.com\/watch?v=heLm9gSVSTc\">jusqu\u2019au bout de la nuit<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"toc-conclusion\">Conclusion<\/h2>\n\n\n\n<p>Le VHDL est tr\u00e8s verbeux, les \u00e9volutions du langage ont tent\u00e9 de corriger un peu le tir mais cela reste tout de m\u00eame verbeux. Certaine caract\u00e9ristiques comme l\u2019insensibilit\u00e9 \u00e0 la casse font un peu penser \u00e0 un langage d\u2019un autre \u00e2ge. Cependant, l\u2019h\u00e9ritage du langage Ada fait de VHDL un langage tr\u00e8s strict et d\u00e9terministe de part sa conception contrairement au <a href=\"https:\/\/insights.sigasi.com\/opinion\/jan\/verilogs-major-flaw\/\">Verilog<\/a>.<\/p>\n\n\n\n<p>Le typage fort peut-\u00eatre consid\u00e9r\u00e9 \u00e0 premi\u00e8re vu comme un d\u00e9faut ralentissant l\u2019\u00e9criture du code. Mais il n\u2019en est rien, apr\u00e8s avoir souffert de \u00ab&nbsp;compiler&nbsp;\u00bb votre porte-gramme pour la simulation, vous aurez l\u2019agr\u00e9able surprise de voir votre syst\u00e8me fonctionner parfaitement sur le FPGA&nbsp;du (presque) premier coup.<\/p>\n\n\n\n<p>Le vocabulaire VHDL est tr\u00e8s vaste et on se contente en g\u00e9n\u00e9ral des structures de code connue dont on sait qu\u2019elles \u00ab&nbsp;synth\u00e9tiseront&nbsp;\u00bb correctement, ce qui donne une impression de ne jamais pouvoir atteindre la ma\u00eetrise du langage.<\/p>\n\n\n\n<p>Il y a quelques ann\u00e9es je m\u2019\u00e9tais <a href=\"https:\/\/linuxfr.org\/users\/martoni\/journaux\/le-vhdl-prend-il-l-eau\">pos\u00e9 la question<\/a> de la popularit\u00e9 du VHDL par rapport au Verilog. En effet, m\u00eame si le VHDL est presque aussi bien support\u00e9 que le Verilog par les outils des constructeurs, \u00e7a n\u2019\u00e9tait pas le cas des logiciels libres. C\u2019est encore largement le cas aujourd\u2019hui, m\u00eame certain logiciels non libre supportent en priorit\u00e9 le Verilog. Le constructeur Gowin par exemple ne permettait que la synth\u00e8se Verilog avec son outil maison. Il fallait installer le logiciel tier <a href=\"https:\/\/www.synopsys.com\/implementation-and-signoff\/fpga-based-design\/synplify-pro.html\">synplify<\/a> de synopsis pour pouvoir acc\u00e9der \u00e0 la synth\u00e8se VHDL.<\/p>\n\n\n\n<p>Cette extension de ghdl pour Yosys change la donne. Car, comme nous l\u2019avons vu, il est possible de l\u2019utiliser pour convertir son projet en Verilog et avoir acc\u00e8s \u00e0 tous l\u2019\u00e9cosyst\u00e8me libre Verilog. Il est \u00e9galement possible de faire de la <a href=\"http:\/\/pepijndevos.nl\/2019\/08\/15\/open-source-formal-verification-in-vhdl.html\">v\u00e9rification formelle pour le VHDL<\/a>.<\/p>\n\n\n\n<p>Avoir la comp\u00e9tence VHDL dans son CV est une assez bonne id\u00e9e car c\u2019est souvent par ce mot que l\u2019on r\u00e9sume le d\u00e9veloppement ASIC\/FPGA\/SoC. En Europe, le VHDL est tr\u00e8s appr\u00e9ci\u00e9 de l\u2019industrie et particuli\u00e8rement de l\u2019industrie de d\u00e9fense.<\/p>\n\n\n\n<p>Mais si c\u2019est juste pour mesurer le tempo, ce n\u2019est peut-\u00eatre pas la voie la plus rapide et la plus simple&nbsp;\ud83d\ude09<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Aller plus loin<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/linuxfr.org\/redirect\/107436\">La vid\u00e9o du projet en action.<\/a> (9 clics)<\/li><li><a href=\"https:\/\/linuxfr.org\/redirect\/107453\">Le d\u00e9pot du projet TapTempoASIC (contenant la version Verilog et VHDL)<\/a> (3 clics)<\/li><li><a href=\"https:\/\/linuxfr.org\/redirect\/107466\">Le d\u00e9pot du projet d&rsquo;extension \u00e0 Yosys pour la synth\u00e8se avec GHDL<\/a> (1 clic)<\/li><li><a href=\"https:\/\/linuxfr.org\/redirect\/107467\">Le d\u00e9pot de GHDL, pour la synth\u00e8se et la simulation VHDL<\/a> (1 clic)<\/li><li><a href=\"https:\/\/linuxfr.org\/redirect\/107468\">Le d\u00e9pot de Yosys, pour la synth\u00e8se Verilog<\/a> (1 clic)<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>[D\u00e9p\u00eache publi\u00e9e initialement sur LinuxFr] Ayant pr\u00e9par\u00e9 tout le mat\u00e9riel pour faire du TapTempo en Verilog, il \u00e9tait trop tentant de r\u00e9aliser la m\u00eame chose en\u00a0VHDL. L\u2019occasion de se plonger dans ce langage de description mat\u00e9riel concurrent du Verilog.L\u2019occasion \u00e9galement de parler des avanc\u00e9es de GHDL, un simulateur libre du VHDL, et d\u00e9sormais \u00e9galement capable &hellip; <a href=\"https:\/\/www.fabienm.eu\/flf\/portage-de-taptempo-en-vhdl\/\" class=\"more-link\">Continuer la lecture de <span class=\"screen-reader-text\">Portage de TapTempo en VHDL<\/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":[22,3,11],"tags":[59,72,187,60,45],"class_list":["post-1641","post","type-post","status-publish","format-standard","hentry","category-blog","category-langages","category-vhdl","tag-ghdl","tag-synthese","tag-taptempo","tag-vhdl","tag-yosys"],"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":"[D\u00e9p\u00eache publi\u00e9e initialement sur LinuxFr] Ayant pr\u00e9par\u00e9 tout le mat\u00e9riel pour faire du TapTempo en Verilog, il \u00e9tait trop tentant de r\u00e9aliser la m\u00eame chose en\u00a0VHDL. L\u2019occasion de se plonger dans ce langage de description mat\u00e9riel concurrent du Verilog.L\u2019occasion \u00e9galement de parler des avanc\u00e9es de GHDL, un simulateur libre du VHDL, et d\u00e9sormais \u00e9galement capable\u2026","_links":{"self":[{"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/1641","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=1641"}],"version-history":[{"count":1,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/1641\/revisions"}],"predecessor-version":[{"id":1642,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/1641\/revisions\/1642"}],"wp:attachment":[{"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/media?parent=1641"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/categories?post=1641"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/tags?post=1641"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}