{"id":1597,"date":"2020-09-20T20:05:53","date_gmt":"2020-09-20T19:05:53","guid":{"rendered":"http:\/\/www.fabienm.eu\/flf\/?p=1597"},"modified":"2020-09-21T13:25:09","modified_gmt":"2020-09-21T12:25:09","slug":"taptempo-en-verilog","status":"publish","type":"post","link":"http:\/\/www.fabienm.eu\/flf\/taptempo-en-verilog\/","title":{"rendered":"TapTempo en Verilog"},"content":{"rendered":"\n<p>Il y a plus de deux ans et demi maintenant, <a href=\"https:\/\/linuxfr.org\/users\/mzf\">mzf<\/a> publiait <a href=\"https:\/\/linuxfr.org\/users\/mzf\/journaux\/un-tap-tempo-en-ligne-de-commande\">un journal sur le site LinuxFR<\/a> parlant de son projet \u00abTapTempo\u00bb. L&rsquo;objectif de son programme \u00e9tait simplement de mesurer la cadence d&rsquo;une musique en tapant sur une touche de son clavier, le r\u00e9sultat s&rsquo;affichant simplement dans la console.<\/p>\n\n\n\n<p>Ce journal fut le point de d\u00e9part d&rsquo;une s\u00e9rie de \u00abprojets TapTempo\u00bb propos\u00e9 par les lecteurs du site dans \u00e0 peu pr\u00eat tous les langages informatique possible&#8230; Mais pas le Verilog. <\/p>\n\n\n\n<p>Voici donc la lacune combl\u00e9e avec TapTempo en Verilog.<\/p>\n\n\n\n<p><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog\">[D\u00e9p\u00eache parue initialement sur LinuxFR]<\/a><\/p>\n\n\n\n<p>Le projet TapTempo semble faiblir depuis quelques mois maintenant. En panne de langage informatique pour en faire une d\u00e9p\u00eache\u202f?<br>Laissez\u2011moi vous pr\u00e9senter un langage assez particulier puisqu\u2019il ne sert pas \u00e0 faire de la programmation. Ce langage permet de d\u00e9crire le comportement num\u00e9rique d\u2019un composant \u00e9lectronique (on parle alors de langage de description de mat\u00e9riel \u2014&nbsp;<a href=\"https:\/\/fr.wikipedia.org\/wiki\/Langage_de_description_de_mat%C3%A9riel\">HDL<\/a>)&nbsp;: le&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Verilog\">Verilog<\/a>.<\/p>\n\n\n\n<p>C\u2019est aussi un langage utilis\u00e9 pour faire de la synth\u00e8se num\u00e9rique sur les circuits logiques programmables (FPGA). Dans cet exemple, nous utiliserons la carte de d\u00e9veloppement \u00e0 bas co\u00fbt <a href=\"https:\/\/github.com\/q3k\/chubby75\/blob\/master\/5a-75b\/hardware_V7.0.md\">ColorLight&nbsp;5A\u201175B<\/a>.<\/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'ensemble du montage TapTempo\" title=\"Source : http:\/\/fabienm.eu\/partage\/taptempocolorlight.JPG\"\/><\/figure>\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\/taptempo-en-verilog#toc-le-verilog\">Le Verilog<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-architecture-de-taptempo\">Architecture de TapTempo<\/a><ul><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-entr%C3%A9e-sortie\">Entr\u00e9e sortie<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-pulsation-de-temporisation-timepulse\">Pulsation de temporisation (<\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/timepulse.v\">timepulse<\/a>)<\/li><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-gestion-des-rebonds-debounce\">Gestion des rebonds (<\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/debounce.v\">debounce<\/a>)<\/li><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-mesure-de-la-p%C3%A9riode-de-tempo-percount\">Mesure de la p\u00e9riode de tempo (<\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/percount.v\">percount<\/a>)<\/li><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-calcul-de-la-fr%C3%A9quence-en-beat-per-minute-per2bpm\">Calcul de la fr\u00e9quence en Beat Per Minute (<\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/per2bpm.v\">per2bpm<\/a>)<\/li><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-g%C3%A9n%C3%A9ration-de-la-tension-de-sortie-pwmgen\">G\u00e9n\u00e9ration de la tension de sortie (<\/a><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/pwmgen.v\">pwmgen<\/a>)<\/li><\/ul><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-simulation-de-lensemble-avec-cocotb\">Simulation de l\u2019ensemble avec Cocotb<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-synth%C3%A8se-sur-colorlight\">Synth\u00e8se sur ColorLight<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-exercices-de-travaux-pratiques\">Exercices de travaux pratiques<\/a><\/li><li><a href=\"https:\/\/linuxfr.org\/news\/taptempo-en-verilog#toc-conclusion\">Conclusion<\/a><\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"toc-le-verilog\">Le Verilog<\/h2>\n\n\n\n<p>Le Verilog est un langage con\u00e7u \u00e0 l\u2019origine pour r\u00e9diger des sp\u00e9cifications de circuits logiques en \u00e9lectronique num\u00e9rique. Le langage permet de d\u00e9crire le comportement de sortie par rapport \u00e0 des entr\u00e9es logiques.<\/p>\n\n\n\n<p>Un peu comme les logiciels de saisie de sch\u00e9ma \u00e9lectronique, le Verilog est tr\u00e8s hi\u00e9rarchique, on d\u00e9crit des modules avec leurs entr\u00e9es-sorties. Que l\u2019on assemble ensuite dans d\u2019autres modules pour finir dans un module \u00ab\u202ftop\u202f\u00bb qui d\u00e9crit le composant final.<\/p>\n\n\n\n<p>Dans le cas de TapTempo, le module \u00ab&nbsp;top&nbsp;\u00bb est d\u00e9clar\u00e9 <a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/taptempo.v\">comme ceci<\/a>&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>module taptempo #(\n    parameter CLK_PER_NS = 40, \/\/ 25Mhz clock (ns)\n    parameter TP_CYCLE = 5120, \/\/ timepulse cycle period (ns)\n    parameter BPM_MAX = 250 \/\/ BPM max (bpm)\n)(\n    input clk_i,\n    input btn_i,\n    output pwm_o\n);\n\/\/corps du module\nendmodule<\/code><\/pre>\n\n\n\n<p>Le module poss\u00e8de deux entr\u00e9es&nbsp;: 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. Les param\u00e8tres seront vus comme des constantes au moment de la simulation, ils permettent de configurer les composants en fonction de la cible.<\/p>\n\n\n\n<p>Le changement de valeur des signaux se fait dans des processus qui sont d\u00e9clench\u00e9s sur \u00e9v\u00e9nement. Ces processus sont d\u00e9crits au moyen du mot clef <code>always@()<\/code> en Verilog.<br>Par exemple, dans le code suivant:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* Detect rising edge*\/\nreg btn_old, btn_rise;\nalways@(posedge clk_i)\nbegin\n    btn_old &lt;= btn_i;\n    if(btn_old == 0 &amp;&amp; btn_i == 1)\n        btn_rise &lt;= 1;\n    else\n        btn_rise &lt;= 0;\nend<\/code><\/pre>\n\n\n\n<p>L\u2019\u00e9v\u00e9nement d\u00e9clencheur du process est le front montant de l\u2019horloge <code>clk_i<\/code>. \u00c0 chaque fois qu\u2019un front montant d\u2019horloge se pr\u00e9sente, le processus est ex\u00e9cut\u00e9 de mani\u00e8re s\u00e9quentielle.<\/p>\n\n\n\n<p>L\u2019op\u00e9rateur <code>&lt;=<\/code> est l\u2019op\u00e9rateur d\u2019affectation dit \u00ab\u202fnon bloquant\u202f\u00bb. Cela signifie que la valeur ne sera effectivement appliqu\u00e9e qu\u2019a la fin de l\u2019ex\u00e9cution du process. Donc la valeur du signal <code>btn_old<\/code> ne sera pas n\u00e9cessairement \u00e9gale \u00e0 btn_i \u00e0 la ligne du <code>if()<\/code> comme on aurait pu instinctivement le croire.<\/p>\n\n\n\n<p>Le langage Verilog a beaucoup de succ\u00e8s dans le monde du logiciel libre. En effet il est relativement peu verbeux et ressemble au C pour de nombreux aspects.<\/p>\n\n\n\n<p>Il est par exemple possible de d\u00e9crire des macros de la m\u00eame mani\u00e8re qu\u2019en C, il suffit de remplacer le symbole # par ` pour cr\u00e9er des constantes qui seront remplac\u00e9es par le pr\u00e9-processeur.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* count tap period *\/\n`define MIN_NS 60_000_000_000\n`define BTN_PER_MAX (`MIN_NS\/TP_CYCLE)\n`define BTN_PER_SIZE ($clog2(1 + `BTN_PER_MAX))<\/code><\/pre>\n\n\n\n<p>Le Verilog reprend \u00e9galement les op\u00e9rateurs bool\u00e9en et binaire &amp;,&amp;&amp;, |,||, etc. du C.<\/p>\n\n\n\n<p>C\u2019est le langage HDL le mieux support\u00e9 par les diff\u00e9rents logiciels libres. Si l\u2019on souhaite se lancer dans le domaine des FPGA et\/ou des ASIC, il est pr\u00e9f\u00e9rable de commencer par lui. C\u2019est \u00e9galement le langage \u00ab\u202fde sortie\u202f\u00bb de quasiment tous les g\u00e9n\u00e9rateurs de code HDL.<\/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\u2019outil indispensable pour commencer un projet en Verilog est\u2026 le papier et le crayon. Il est en effet indispensable d\u2019avoir une vue d\u2019ensemble assez claire de ce que l\u2019on souhaite r\u00e9aliser avant de se lancer dans le code.<\/p>\n\n\n\n<p>Voici donc l\u2019architecture g\u00e9n\u00e9rale du composant TapTempo :<\/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 TapTempo au Crayon\" title=\"Source : http:\/\/fabienm.eu\/partage\/schema_taptempo_crayon.jpg\"\/><\/figure>\n\n\n\n<p>M\u00eame si l\u2019on doit revenir plusieurs fois (ce qui est le cas ici puisque les constantes ne sont pas \u00e0 jour) sur ce sch\u00e9ma g\u00e9n\u00e9ral en cours de d\u00e9veloppement, cette partie est tr\u00e8s importante. Si elle est bien pens\u00e9e, le reste coule de source.<\/p>\n\n\n\n<p>Le composant va n\u00e9cessiter quelques compteurs, mais l\u2019horloge utilis\u00e9e ici \u00e9tant tr\u00e8s rapide nous allons d\u2019abord factoriser le comptage au moyen du module nomm\u00e9 <code>timepulse<\/code>, ce module va distribuer un pulse qui servira de base aux autres compteurs pour leur fonctionnement interne.<\/p>\n\n\n\n<p>L\u2019entr\u00e9e utilisateur se compose d\u2019un bouton (touche t\u00e9l\u00e9graphique \u00ab\u202fmorse\u202f\u00bb). Les fronts montant et descendant de cette entr\u00e9e n\u2019\u00e9tant pas synchronis\u00e9s sur l\u2019horloge du syst\u00e8me nous allons devoir le faire au moyen de deux bascules en s\u00e9rie pour \u00e9viter la <a href=\"https:\/\/en.wikipedia.org\/wiki\/Metastability_(electronics)\">m\u00e9tastabilit\u00e9<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* Synchronize btn_i to avoid metastability*\/\nreg btn_old, btn_s;\nalways@(posedge clk_i or posedge rst)\nbegin\n    if(rst) begin\n        btn_old &lt;= 1'b0;\n        btn_s &lt;= 1'b0;\n    end else begin\n        btn_old &lt;= btn_i;\n        btn_s &lt;= btn_old;\n    end\nend<\/code><\/pre>\n\n\n\n<p>Le second probl\u00e8me que pose notre entr\u00e9e est que l\u2019appui sur le bouton ne g\u00e9n\u00e8re pas des changements francs de son \u00e9tat. Chaque \u00ab\u00a0appui et rel\u00e2che\u00a0\u00bb g\u00e9n\u00e8re une s\u00e9rie de rebonds et donc une s\u00e9rie de 0 et de 1 avant de se stabiliser. Pour lisser le signal il va donc falloir faire passer le signal dans le bloc \u00ab\u202fanti-rebond\u202f\u00bb <code>debounce<\/code>.<\/p>\n\n\n\n<p>Le bloc <code>percount<\/code> va ensuite se charger de mesurer le temps entre deux appuis sur le bouton. Cette p\u00e9riode va devoir \u00eatre transform\u00e9e en fr\u00e9quence \u00ab\u202fBPM\u202f\u00bb (Beat Per Minute) via le module <code>per2bpm<\/code> puis en une valeur pseudo-analogique (PWM) gr\u00e2ce au module <code>pwmgen<\/code>.<\/p>\n\n\n\n<p>La carte cible ne poss\u00e9dant pas de bouton \u00ab&nbsp;reset&nbsp;\u00bb, il va falloir le g\u00e9n\u00e9rer gr\u00e2ce au module <code>rstgen<\/code> de mani\u00e8re \u00e0 s\u2019assurer de l\u2019\u00e9tat de d\u00e9part de notre syst\u00e8me au d\u00e9marrage.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-entr\u00e9e-sortie\">Entr\u00e9e sortie<\/h3>\n\n\n\n<p>La plupart des programmes TapTempo propos\u00e9s jusqu\u2019ici supposaient \u2013 en plus d\u2019un CPU \u2013 la pr\u00e9sence d\u2019un clavier et d\u2019une console texte de sortie (avec toute la pile de pilotes et de syst\u00e8me d\u2019exploitation associ\u00e9s). Ici, nous allons devoir tout d\u00e9finir dans le \u00ab\u202f<a href=\"http:\/\/www.fabienm.eu\/flf\/et-pourquoi-pas-portegramme\/\">portegramme<\/a>\u202f\u00bb \u2013 Dans l\u2019industrie on va parler d\u2019IP pour Intellectual Property, quel horrible nom \u2013.<\/p>\n\n\n\n<p>L\u2019id\u00e9e est donc de simplifier au maximum l\u2019entr\u00e9e \u00ab\u202fclavier\u202f\u00bb et la sortie histoire de pouvoir les d\u00e9crire simplement.<\/p>\n\n\n\n<p>Pour l\u2019entr\u00e9e nous allons nous contenter d\u2019un contact de type bouton, ou d\u2019une touche de type t\u00e9l\u00e9graphe \u00ab\u202fmorse\u202f\u00bb.<\/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=\"TapTempoEntreeMorse\" title=\"Source : http:\/\/fabienm.eu\/partage\/taptempo_entree_morse_modif.jpg\"\/><\/figure>\n\n\n\n<p>Comme on peut le voir dans le sch\u00e9ma ci-dessus, quand la touche est appuy\u00e9e, l\u2019entr\u00e9e \u00ab\u202fbouton\u202f\u00bb est mise \u00e0 la masse et donne un niveau logique \u00e0 0 sur notre syst\u00e8me. Lorsque l\u2019on rel\u00e2che le bouton, la r\u00e9sistance de tirage ram\u00e8ne le niveau de tension \u00e0 Vcc pour avoir un niveau 1 sur l\u2019entr\u00e9e.<\/p>\n\n\n\n<p>Pour la sortie, l\u2019id\u00e9e de mettre un \u00e9cran complexifie \u00e9norm\u00e9ment le syst\u00e8me. En effet, il est n\u00e9cessaire de faire une machine d\u2019\u00e9tat assez complexe pour initialiser l\u2019\u00e9cran puis rafra\u00eechir l\u2019affichage. Il est souvent n\u00e9cessaire d\u2019ajouter un processeur \u00ab\u202fsoft\u202f\u00bb rien que pour \u00e7a d\u2019ailleurs. (Bon il est vrai que le VGA n\u2019est pas si compliqu\u00e9, mais il reste plus complexe que la solution propos\u00e9e ici).<\/p>\n\n\n\n<p>Non, l\u2019id\u00e9e ici est d\u2019utiliser les graduations de l\u2019antique voltm\u00e8tre \u00e0 aiguille trouv\u00e9 dans une cave et qui gradue de 0 \u00e0 300 comme on peut le voir sur la photo :<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/img.linuxfr.org\/img\/687474703a2f2f66616269656e6d2e65752f706172746167652f766f6c746d657472655f74617074656d706f2e6a7067\/voltmetre_taptempo.jpg\" alt=\"vuemetretaptempo\" title=\"Source : http:\/\/fabienm.eu\/partage\/voltmetre_taptempo.jpg\"\/><\/figure>\n\n\n\n<p>Et comme un syst\u00e8me num\u00e9rique ne sort que des 0 et des 1 sur ses broches, on va \u00ab\u202fsimuler\u202f\u00bb une valeur analogique au moyen d\u2019une PWM (Pulse With Modulation). Il suffit de changer le rapport cyclique entre le temps haut et le temps bas de notre signal pour faire varier la tension moyenne qui sera vue par le voltm\u00e8tre. Si on l\u2019ajuste correctement avec une r\u00e9sistance en s\u00e9rie, il est relativement facile de forcer la valeur maximale (5V) \u00e0 250.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/img.linuxfr.org\/img\/687474703a2f2f66616269656e6d2e65752f706172746167652f70776d5f74617074656d706f2e6a7067\/pwm_taptempo.jpg\" alt=\"pwm\" title=\"Source : http:\/\/fabienm.eu\/partage\/pwm_taptempo.jpg\"\/><\/figure>\n\n\n\n<p>La p\u00e9riode de la pwm sera configur\u00e9e suffisamment rapide pour que l\u2019aiguille n\u2019oscille pas.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-pulsation-de-temporisation-timepulse\">Pulsation de temporisation (<a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/timepulse.v\">timepulse<\/a>)<\/h3>\n\n\n\n<p>Le module ne prend pas de valeur d\u2019entr\u00e9e hormis l\u2019horloge et le reset qui sont de rigueur dans tout le projet. Son signal de sortie <code>tp_o<\/code> est une pulsation de l\u2019horloge \u00e9mise toutes les 5120 ns\u202f:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>module timepulse #(\n    parameter CLK_PER_NS = 40,\n    parameter PULSE_PER_NS = 5120\n)(\n    \/* clock and reset *\/\n    input clk_i,\n    input rst_i,\n    \/* output *\/\n    output tp_o);<\/code><\/pre>\n\n\n\n<p>Pour pouvoir compter des p\u00e9riodes de 5120ns on d\u00e9finit un registre de comptage :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>`define MAX_COUNT (PULSE_PER_NS\/CLK_PER_NS)\n`define MAX_COUNT_SIZE ($clog2(`MAX_COUNT))\n\nreg &#91;`MAX_COUNT_SIZE-1:0] counter = 0;<\/code><\/pre>\n\n\n\n<p>Puis on compte de mani\u00e8re synchronis\u00e9e avec l\u2019horloge&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>always@(posedge clk_i or posedge rst_i)\nbegin\n    if(rst_i)\n    begin\n        counter &lt;= 0;\n    end else begin\n        if (counter &lt; `MAX_COUNT)\n        begin\n            counter &lt;= counter + 1'b1;\n        end else begin\n            counter &lt;= 0;\n        end\n    end\nend<\/code><\/pre>\n\n\n\n<p>La pulsation est \u00e9mise lorsque le compteur passe par 0\u202f:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>assign tp_o = (counter == 0);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-gestion-des-rebonds-debounce\">Gestion des rebonds (<a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/debounce.v\">debounce<\/a>)<\/h3>\n\n\n\n<p>L\u2019entr\u00e9e de ce module est le signal de bouton pr\u00e9alablement synchronis\u00e9 avec l\u2019horloge du syst\u00e8me <code>btn_s<\/code>. Le compteur utilisera la pulsation <code>tp_i<\/code> g\u00e9n\u00e9r\u00e9 par le module timepulse d\u00e9crit ci-avant.<\/p>\n\n\n\n<p>La sortie du module est un signal btn_o proprement liss\u00e9. La p\u00e9riode de temporisation de 20\u202fms est donn\u00e9 ici en param\u00e8tre <code>DEBOUNCE_PER_NS<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>module debounce #(\n    parameter PULSE_PER_NS = 5120,\n    parameter DEBOUNCE_PER_NS = 20_971_520\n)(\n    \/* clock and reset *\/\n    input clk_i,\n    input rst_i,\n    \/* inputs *\/\n    input tp_i,\n    input btn_i,\n    \/* output *\/\n    output btn_o\n);<\/code><\/pre>\n\n\n\n<p>La gestion des rebonds est r\u00e9alis\u00e9e au moyen d\u2019un compteur utilis\u00e9 pour temporiser.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>`define MAX_COUNT ((DEBOUNCE_PER_NS\/PULSE_PER_NS)-1'b1)\n`define MAX_COUNT_SIZE ($clog2(`MAX_COUNT))\n\n\/* Counter *\/\nreg &#91;`MAX_COUNT_SIZE-1:0] counter = 0;<\/code><\/pre>\n\n\n\n<p>Ainsi que d\u2019une machine d\u2019\u00e9tats \u00e0 4 \u00e9tats\u202f:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* State machine *\/\nlocalparam &#91;1:0] s_wait_low  = 2'h0,\n                 s_wait_high = 2'h1,\n                 s_cnt_high  = 2'h2,\n                 s_cnt_low   = 2'h3;\n\nreg &#91;1:0] state_reg, state_next;<\/code><\/pre>\n\n\n\n<p>Les transitions de la machine d\u2019\u00e9tats sont donn\u00e9es dans le code ci-dessous dans un processus dit \u00ab\u202fcombinatoire\u202f\u00bb (<code>always@*<\/code>) par opposition \u00e0 un processus \u00ab\u202fsynchrone\u202f\u00bb.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>always@*\nbegin\n    case(state_reg)\n        s_wait_low:\n            if(btn_i)\n                state_next = s_cnt_high;\n            else\n                state_next = s_wait_low;\n        s_wait_high:\n            if(!btn_i)\n                state_next = s_cnt_low;\n            else\n                state_next = s_wait_high;\n        s_cnt_high:\n            \/* verilator lint_off WIDTH *\/\n            if(counter == `MAX_COUNT)\n            \/* verilator lint_on WIDTH *\/\n                state_next = s_wait_high;\n            else\n                state_next = s_cnt_high;\n        s_cnt_low:\n            \/* verilator lint_off WIDTH *\/\n            if(counter == `MAX_COUNT)\n            \/* verilator lint_on WIDTH *\/\n                state_next = s_wait_low;\n            else\n                state_next = s_cnt_low;\n    endcase;\nend<\/code><\/pre>\n\n\n\n<p>L\u2019\u00e9tat de la machine est tout de m\u00eame synchronis\u00e9 dans un second processus :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>always@(posedge clk_i or posedge rst_i)\n    if(rst_i)\n        state_reg &lt;= s_wait_low;\n    else\n        state_reg &lt;= state_next;<\/code><\/pre>\n\n\n\n<p>Le principe de \u00ab\u202flissage\u202f\u00bb des rebonds est donc le suivant\u202f: Dans l\u2019\u00e9tat initial <code>s_wait_low<\/code> on attend que le bouton passe \u00e0 la valeur 1. Lorsque le signal passe \u00e0 1, on change d\u2019\u00e9tat pour <code>s_cnt_high<\/code>.<\/p>\n\n\n\n<p>Le passage dans l\u2019\u00e9tat <code>s_cnt_high<\/code> a pour effet de faire passer le signal de sortie \u00e0 1 et d\u00e9clencher le compteur. Tant que le compteur compte et n\u2019a pas atteint la valeur <code>MAX_COUNT<\/code>, on reste dans cet \u00e9tat quelles que soient les variations du signal d\u2019entr\u00e9e.<br>Lorsque le compteur atteint la valeur maximale, la machine d\u2019\u00e9tat passe dans l\u2019\u00e9tat <code>s_wait_high<\/code> (en attente de valeurs hautes).<\/p>\n\n\n\n<p>Dans l\u2019\u00e9tat <code>s_wait_high<\/code> on surveille la valeur du bouton d\u2019entr\u00e9e, si elle passe \u00e0 0 on change d\u2019\u00e9tat pour <code>s_cnt_low<\/code>.<\/p>\n\n\n\n<p>De mani\u00e8re sym\u00e9trique \u00e0 <code>s_cnt_high<\/code> on d\u00e9clenche donc le compteur en ignorant la valeur d\u2019entr\u00e9e. Et, lorsqu\u2019elle atteint son maximum on passe \u00e0 l\u2019\u00e9tat initial <code>s_wait_low<\/code>.<\/p>\n\n\n\n<p>La valeur \u00ab\u202fliss\u00e9e\u202f\u00bb du bouton en sortie est donn\u00e9e par l\u2019\u00e9tat de la machine d\u2019\u00e9tat&nbsp;:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>assign btn_o = (state_reg == s_cnt_high) || (state_reg == s_wait_high);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-mesure-de-la-p\u00e9riode-de-tempo-percount\">Mesure de la p\u00e9riode de tempo (<a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/percount.v\">percount<\/a>)<\/h3>\n\n\n\n<p>L\u2019interface du module <code>percount<\/code> se compose des entr\u00e9es habituelles d\u2019horloge <code>clk_i<\/code>, de reset <code>rst_i<\/code> ainsi que de la pulsation <code>tp_i<\/code>.<\/p>\n\n\n\n<p>Le signal de mesure en entr\u00e9e est <code>btn_i<\/code> et la sortie est un vecteur <code>btn_per_o<\/code> donnant la valeur mesur\u00e9e. La valeur est consid\u00e9r\u00e9e comme valide uniquement lorsque la sortie <code>btn_per_valid<\/code> est \u00e0 1. Cette astuce permet d\u2019\u00e9conomiser un registre si la sauvegarde de la valeur mesur\u00e9e est inutile comme c\u2019est le cas ici.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>`define MIN_NS 60_000_000_000\n`define BTN_PER_MAX (`MIN_NS\/TP_CYCLE)\n`define BTN_PER_SIZE ($clog2(1 + `BTN_PER_MAX))\n\nmodule percount #(\n    parameter CLK_PER_NS = 40,\n    parameter TP_CYCLE = 5120,\n    parameter PULSE_PER_NS = 5120,\n)(\n    \/* clock and reset *\/\n    input clk_i,\n    input rst_i,\n    \/* time pulse *\/\n    input tp_i,\n    \/* input button *\/\n    input btn_i,\n    \/* output period *\/\n    output &#91;(`BTN_PER_SIZE-1):0] btn_per_o,\n    output btn_per_valid);<\/code><\/pre>\n\n\n\n<p>Maintenant que nous avons un signal de bouton btn_b propre et liss\u00e9, nous pouvons entamer la mesure de la p\u00e9riode entre deux appuis au moyen de\u2026 devinez quoi&nbsp;? D\u2019un compteur pardi&nbsp;!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>reg &#91;($clog2(`BTN_PER_MAX+1)-1):0] counter = 0;\nreg counter_valid = 0;\n\nassign btn_per_valid = counter_valid;\nassign btn_per_o = counter;<\/code><\/pre>\n\n\n\n<p>Il nous faut tout d\u2019abord d\u00e9tecter le front descendant du bouton \u202f:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>reg btn_old;\nwire btn_fall = btn_old &amp; (!btn_i);\n\nalways@(posedge clk_i or posedge rst_i)\nbegin\n    if(rst_i)\n        btn_old &lt;= 1'b0;\n    else\n        btn_old &lt;= btn_i;     \nend<\/code><\/pre>\n\n\n\n<p>Le signal <code>btn_fall<\/code> sert de remise \u00e0 z\u00e9ro du compteur ainsi que de validation de la valeur de sortie :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>always@(posedge clk_i or posedge rst_i)\nbegin\n    if(rst_i)\n    begin\n        counter &lt;= 0;\n    end else begin\n        if(btn_fall) begin\n            counter_valid &lt;= 1'b1;\n        end else if(counter_valid) begin\n            counter &lt;= 0;\n            counter_valid &lt;= 1'b0;\n        end else begin\n            \/* stop counting if max, count tp_i *\/\n            if(tp_i &amp;&amp; counter &lt; `BTN_PER_MAX)\n                counter &lt;= counter + 1'b1;\n        end\n    end\nend<\/code><\/pre>\n\n\n\n<p>Le compteur compte le nombre de pulsations de <code>tp_i<\/code> jusqu\u2019\u00e0 atteindre la saturation <code>BTN_PER_MAX<\/code>. Si un front montant du bouton se pr\u00e9sente avec <code>btn_fall<\/code>, on valide le compteur avec <code>counter_valid<\/code>. Et si le signal de validation passe \u00e0 1 (donc le coup d\u2019horloge suivant) on remet le compteur \u00e0 z\u00e9ro et on recommence \u00e0 compter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-calcul-de-la-fr\u00e9quence-en-beat-per-minute-per2bpm\">Calcul de la fr\u00e9quence en Beat Per Minute (<a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/per2bpm.v\">per2bpm<\/a>)<\/h3>\n\n\n\n<p>Avec le module <code>per2bpm<\/code> on arrive dans la partie critique du projet, car il va nous falloir faire une division. On entre une p\u00e9riode dans le module :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/* inputs *\/\n    input &#91;(`BTN_PER_SIZE-1):0] btn_per_i,\n    input btn_per_valid,<\/code><\/pre>\n\n\n\n<p>Et on doit en ressortir une fr\u00e9quence (BPM) :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/* outputs *\/\n    output &#91;`BPM_SIZE - 1:0] bpm_o,\n    output bpm_valid<\/code><\/pre>\n\n\n\n<p>Suivant la formule :<\/p>\n\n\n\n<p>Il faut donc diviser la constante<\/p>\n\n\n\n<p>par la variable <img decoding=\"async\" src=\"\" alt=\"btn\\_per\\_i\"><\/p>\n\n\n\n<p>La division (tout comme la multiplication) est un point sensible en Verilog. En effet, l\u2019op\u00e9rateur de division existe bien dans le langage et il se peut que cela simule parfaitement.<\/p>\n\n\n\n<p>C\u2019est lorsque arrivera l\u2019\u00e9tape de la synth\u00e8se que l\u2019on risque d\u2019avoir quelques surprises. Il est possible que certains logiciels de synth\u00e8se r\u00e9ussiront \u00e0 faire quelque chose en un coup d\u2019horloge. Mais il est certain que cela se fera au prix de tr\u00e8s mauvaises performances en mati\u00e8re de ressources utilis\u00e9es et de fr\u00e9quence d\u2019horloge. Il est surtout probable que votre logiciel de synth\u00e8se jette l\u2019\u00e9ponge.<\/p>\n\n\n\n<p>Pour r\u00e9aliser cette division, nous allons donc en revenir aux fondamentaux appris au primaire et poser la division. Une division, c\u2019est la recherche du Quotient et du Reste de l\u2019\u00e9quation suivante :<\/p>\n\n\n\n\n\n<pre class=\"wp-block-code\"><code>reg &#91;(`REGWIDTH-1):0] divisor;\nreg &#91;(`REGWIDTH-1):0] remainder;\nreg &#91;(`REGWIDTH-1):0] quotient;<\/code><\/pre>\n\n\n\n<p>La taille des registres sera celle de la p\u00e9riode en entr\u00e9e <code>BTN_PER_SIZE<\/code> additionn\u00e9 \u00e0 la constante \u00e0 diviser.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>`define DIVIDENTWITH ($clog2(1 + `MIN_NS\/(TP_CYCLE)))\n`define REGWIDTH (`BTN_PER_SIZE + `DIVIDENTWITH)<\/code><\/pre>\n\n\n\n<p>La division s\u2019effectue avec une s\u00e9rie de soustraction du reste (<code>remainder<\/code>) et de d\u00e9calage du diviseur.<\/p>\n\n\n\n<p>\u00c0 l\u2019\u00e9tape initiale, on place le diviseur \u00e0 gauche du registre <code>divisor<\/code> et le dividende dans le reste <code>remainder<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>     divisor &lt;= {btn_per_i, (`DIVIDENTWITH)'h0};\n     remainder &lt;= `MIN_NS\/TP_CYCLE;\n     \/\/ le r\u00e9sultat est initialis\u00e9 \u00e0 0:\n     quotient &lt;= 0;<\/code><\/pre>\n\n\n\n<p>Puis on effectue une s\u00e9rie de comparaison-soustraction-d\u00e9calage avec l\u2019algorithme comme d\u00e9crit ci-dessous&nbsp;:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>si le diviseur (<code>divisor<\/code>) inf\u00e9rieur ou \u00e9gal au reste (<code>remainder<\/code>), on soustrait le reste avec le diviseur et on d\u00e9cale le quotient \u00e0 gauche en ajoutant 1 :<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>            if(divisor &lt;= remainder)\n            begin\n                remainder &lt;= remainder - divisor;\n                quotient &lt;= {quotient&#91;(`DIVIDENTWITH-2):0], 1'b1};<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>si le diviseur (<code>divisor<\/code>) est sup\u00e9rieur au reste, on d\u00e9cale le quotient \u00e0 gauche en ajoutant 0. On ne touche pas au reste&nbsp;:<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>                quotient &lt;= {quotient&#91;(`DIVIDENTWITH-2):0], 1'b0};<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>dans tous les cas, on d\u00e9cale le diviseur \u00e0 droite.<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>            divisor &lt;= {1'b0, divisor&#91;(`REGWIDTH-1):1]};<\/code><\/pre>\n\n\n\n<p>La division est orchestr\u00e9e par une machine \u00e0 trois \u00e9tats :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>localparam &#91;1:0] s_init    = 2'h0,\n                 s_compute = 2'h1,\n                 s_result  = 2'h2;\n\nreg &#91;1:0] state_reg, state_next;<\/code><\/pre>\n\n\n\n<p>Et le r\u00e9sultat est disponible en sortie quand <code>state_reg<\/code> est dans l\u2019\u00e9tat <code>s_result<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>assign bpm_o = quotient&#91;(`BPM_SIZE-1):0];\nassign bpm_valid = (state_reg == s_result);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"toc-g\u00e9n\u00e9ration-de-la-tension-de-sortie-pwmgen\">G\u00e9n\u00e9ration de la tension de sortie (<a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/pwmgen.v\">pwmgen<\/a>)<\/h3>\n\n\n\n<p>La g\u00e9n\u00e9ration du signal pseudo analogique d\u00e9crite en introduction est presque la partie la plus simple.<\/p>\n\n\n\n<p>On compte (oui encore) de 0 \u00e0 250 (BPM_MAX) :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* count *\/\nalways@(posedge clk_i or posedge rst_i)\nbegin\n    if(rst_i)\n        count &lt;= BPM_MAX;\n    else begin\n        if(tp_i)\n        begin\n            if (count == 0)\n                count &lt;= BPM_MAX;\n            else\n                count &lt;= count - 1'b1;\n        end\n    end\nend<\/code><\/pre>\n\n\n\n<p>Et on passe le signal de sortie pwm_o \u00e0 1 lorsque le compteur est inf\u00e9rieur \u00e0 la fr\u00e9quence demand\u00e9e :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>assign pwm_o = (count &lt;= pwmthreshold);<\/code><\/pre>\n\n\n\n<p>Il y a juste une subtilit\u00e9 consistant \u00e0 sauvegarder la valeur de la fr\u00e9quence donn\u00e9e en entr\u00e9e dans deux registres <code>pwmthreshold<\/code> et bpm_reg :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>reg &#91;($clog2(BPM_MAX+1)-1):0] bpm_reg;\nreg &#91;($clog2(BPM_MAX+1)-1):0] pwmthreshold;\n\n\/* Latching bpm_i on bpm_valid *\/\nalways@(posedge clk_i or posedge rst_i)\nbegin\n    if(rst_i)\n    begin\n        bpm_reg &lt;= 0;\n        pwmthreshold &lt;= 0;\n    end else begin\n        if(bpm_valid)\n            bpm_reg &lt;= bpm_i;\n        if(count == BPM_MAX)\n            pwmthreshold &lt;= bpm_reg;\n    end\nend<\/code><\/pre>\n\n\n\n<p>Le premier registre <code>bpm_reg<\/code> est mis \u00e0 jour lorsque le signal d\u2019entr\u00e9e <code>bpm_valid<\/code> est \u00e0 1. Pour m\u00e9moriser la valeur d\u2019entr\u00e9e et pouvoir l\u2019utiliser au moment o\u00f9 l\u2019on en a besoin.<br>Et le second <code>pwmthreshold<\/code> est rafra\u00eechi en fin de cycle d\u2019une p\u00e9riode de la pwm. Pour \u00e9viter d\u2019avoir un changement de valeur en cours de p\u00e9riode, et donc un rapport cyclique faux.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"toc-simulation-de-lensemble-avec-cocotb\">Simulation de l\u2019ensemble avec Cocotb<\/h2>\n\n\n\n<p>Jusqu\u2019ici nous avons d\u00e9crit le comportement du composant final en Verilog. Toutes les d\u00e9veloppeuses ou d\u00e9veloppeurs HDL le savent tr\u00e8s bien, il est impossible de r\u00e9aliser un projet Verilog (ou autre HDL) sans faire un minimum de simulation.<\/p>\n\n\n\n<p>Pour simuler le composant, il est n\u00e9cessaire de d\u00e9crire les stimuli en entr\u00e9e du composant et de lire\/valider les sorties. On va g\u00e9n\u00e9ralement cr\u00e9er un composant hi\u00e9rarchiquement au-dessus du top de notre composant appel\u00e9 \u00ab\u202ftestbench\u202f\u00bb dans lequel nous d\u00e9crirons les changements de valeurs des entr\u00e9es au cours du temps. Cette partie peut tout \u00e0 fait se faire en Verilog.<\/p>\n\n\n\n<p>Cependant, l\u2019id\u00e9e de m\u00e9langer la partie banc de test et composant \u00ab\u202fsynth\u00e9tisable\u202f\u00bb n\u2019est pas terrible. En effet on va tr\u00e8s vite confondre les deux parties et m\u00e9langer les codes. L\u2019exemple de la division est criant\u202f:&nbsp;l\u2019op\u00e9rateur diviser \u00ab\u202f\/\u202f\u00bb fonctionne tr\u00e8s bien dans la partie <code>testbench<\/code> mais elle pose de gros probl\u00e8mes dans la partie \u00ab\u202fsynth\u00e9tisable\u202f\u00bb.<\/p>\n\n\n\n<p>Pour \u00e9viter ce m\u00e9lange des genres, une solution radicale consiste \u00e0 utiliser un autre langage pour la partie banc de test. Le C++ et le <a href=\"https:\/\/accellera.org\/downloads\/standards\/systemchttps:\/\/accellera.org\/downloads\/standards\/systemc\">SystemC<\/a> sont utilis\u00e9s depuis longtemps pour cela. S\u2019ils sont utilis\u00e9s en conjonction avec <a href=\"https:\/\/linuxfr.org\/news\/verilator-4-002\">Verilator<\/a> ils permettent d\u2019atteindre des puissance\/rapidit\u00e9 de simulation in\u00e9gal\u00e9es par les simulateurs \u00ab\u202fpropri\u00e9taires\u202f\u00bb.<\/p>\n\n\n\n<p>Une autre m\u00e9thode consiste \u00e0 piloter le simulateur Verilog avec un autre programme, on parle alors de cosimulation. C\u2019est le c\u0153ur du fonctionnement du module python <a href=\"https:\/\/linuxfr.org\/news\/cocotb-1-4-0-la-maturite\">CocoTB<\/a>. L\u2019id\u00e9e ici est d\u2019\u00e9crire son banc de test en python, ce qui est nettement plus confortable que du Verilog ou m\u00eame du C++ (SystemC est une librairie C++ \u00e9galement).<\/p>\n\n\n\n<p>Le testbench pour simuler l&rsquo;ensemble du projet taptempo se trouve dans le r\u00e9pertoire cocotb\/test_taptempo. Pour le simuler il suffit de s&rsquo;y rendre et d&rsquo;y ex\u00e9cuter un make.<br>\u00c0 condition cependant d\u2019avoir install\u00e9 cocotb (en python3) et <a href=\"https:\/\/github.com\/steveicarus\/iverilog\">Icarus<\/a> pour la partie simulateur (On laissera l\u2019appr\u00e9ciation de l\u2019installation au lecteur en fonction de ses affinit\u00e9s linuxdistributive).<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/cocotb\/test_taptempo\/test_taptempo.py\">La simulation<\/a> consiste \u00e0 tester trois appuis sur le bouton \u00e0 des intervalles diff\u00e9rents\u202f:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@cocotb.test()\nasync def debounce_upanddown(dut):\n    td = TestTapTempo(dut)\n    td.log.info(\"Running test!\")\n    await td.reset()\n    td.log.info(\"System reseted!\")\n    await Timer(1000, units=\"us\")\n    td.log.info(\"up\")\n    await td.bounce_up(10, bounce_per=(10000, \"ns\"))\n    await Timer(24, units=\"ms\")\n    td.log.info(\"down\")\n    await td.bounce_down(10, bounce_per=(10000, \"ns\"))\n    await Timer(300, units=\"ms\")\n    td.log.info(\"up\")\n    await td.bounce_up(10, bounce_per=(10000, \"ns\"))\n    await Timer(30, units=\"ms\")\n    td.log.info(\"down\")\n    await td.bounce_down(10, bounce_per=(10000, \"ns\"))\n    await Timer(800, units=\"ms\")\n    td.log.info(\"up\")\n    await td.bounce_up(10, bounce_per=(10000, \"ns\"))\n    await Timer(30, units=\"ms\")\n\n    td.log.info(\"Wait stable\")\n    await Timer(1000, units=\"us\")<\/code><\/pre>\n\n\n\n<p>Cela g\u00e9n\u00e8re un fichier de \u00ab\u202ftraces\u202f\u00bb au format VCD particuli\u00e8rement volumineux de 2,3\u202fGo (qui se compresse \u00e0 70\u202fMo avec xz\u202f!) permettant de visionner les signaux au cours du temps gr\u00e2ce \u00e0 <a href=\"https:\/\/linuxfr.org\/news\/simplifier-la-visualisation-de-chronogrammes\">gtkwave<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ gtkwave -g taptempo.vcd<\/code><\/pre>\n\n\n\n<p>Et donne la trace suivante :<br><img decoding=\"async\" src=\"https:\/\/img.linuxfr.org\/img\/687474703a2f2f66616269656e6d2e65752f706172746167652f66756c6c5f74617074656d706f5f73696d752e706e67\/full_taptempo_simu.png\" alt=\"simulation_taptempo_full\"><\/p>\n\n\n\n<p>Cette simulation est particuli\u00e8rement longue (il m\u2019a fallu environ une heure et demie sur mon vieux T430) et g\u00e9n\u00e8re un fichier de trace monstrueux. En phase de d\u00e9veloppement on va g\u00e9n\u00e9ralement lancer de petites simulations par modules comme on peut le voir pour le module <code>debounce<\/code> dans le r\u00e9pertoire cocotb\/test_debounce. On changera \u00e9galement certaines constantes de temps pour limiter les \u00ab\u202f\u202fpas\u202f\u00bb de simulation consommant inutilement du calcul processeur.<\/p>\n\n\n\n<p>Il est \u00e9galement possible de laisser l\u2019ordinateur \u00e9crire les stimuli gr\u00e2ce \u00e0 la m\u00e9thode de preuve formelle. C\u2019est la m\u00e9thode qui a \u00e9t\u00e9 utilis\u00e9e ici pour les modules. Les fichiers de configuration se trouvent dans le r\u00e9pertoire formal\/*.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"toc-synth\u00e8se-sur-colorlight\">Synth\u00e8se sur ColorLight<\/h2>\n\n\n\n<p>La <a href=\"https:\/\/fr.aliexpress.com\/item\/32281130824.html\">Colorlight<\/a> n\u2019est pas initialement une carte de d\u00e9veloppement pour les FPGA. C\u2019est une carte permettant de piloter des panneaux de LED qui nous agressent un peu partout dans les rues commer\u00e7antes. Cependant, <a href=\"https:\/\/github.com\/q3k\/chubby75\/tree\/master\/5a-75b\">un petit malin<\/a> s\u2019est rendu compte qu\u2019elle \u00e9tait munie d\u2019un FPGA de chez Lattice : l&rsquo;<a href=\"https:\/\/www.latticesemi.com\/en\/Products\/FPGAandCPLD\/ECP5\">ECP5<\/a>.<\/p>\n\n\n\n<p>Ce FPGA&nbsp;poss\u00e8de deux gros avantages :<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>il est relativement gros, suffisamment pour poss\u00e9der des multiplieurs c\u00e2bl\u00e9s, des s\u00e9rialiseurs-d\u00e9s\u00e9rialiseurs\u2026<\/li><li>on peut d\u00e9velopper dessus avec une cha\u00eene de d\u00e9veloppement int\u00e9gralement opensource&nbsp;!<\/li><\/ul>\n\n\n\n<p>Jusqu\u2019\u00e0 la colorlight, les kits de d\u00e9veloppement ECP5 n\u2019\u00e9taient pas donn\u00e9s puisque l<a href=\"https:\/\/www.latticesemi.com\/en\/Solutions\/Solutions\/SolutionsDetails01\/CommunitySourced\">es premi\u00e8res cartes d\u00e9butaient \u00e0 100 $ minimum<\/a>. Mais avec la colorlight, on tombe \u00e0 15&nbsp;$, ce qui en fait un kit de d\u00e9veloppement ultra bon march\u00e9 pour se faire la main avec des FPGA.<\/p>\n\n\n\n<p>Et comme tout est opensource, il est ais\u00e9 d\u2019aller installer les logiciels permettant de synth\u00e9tiser TapTempo sur sa distribution Linux pr\u00e9f\u00e9r\u00e9e.<br>L\u2019explication de l\u2019installation des outils est hors de propos de cet article (un article d\u00e9taill\u00e9 sur la colorlight est disponible dans le <a href=\"https:\/\/www.hackable.fr\/\">Hackable 35<\/a>), mais une fois les outils install\u00e9s, il suffit de se rendre dans le r\u00e9pertoire <code>synthesis\/colorlight<\/code> du projet et de faire <code>make<\/code> :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ make\n&#91;...]\nInfo: Device utilisation:\nInfo:          TRELLIS_SLICE:   328\/12144     2%\nInfo:             TRELLIS_IO:     3\/  197     1%\nInfo:                   DCCA:     1\/   56     1%\nInfo:                 DP16KD:     0\/   56     0%\nInfo:             MULT18X18D:     0\/   28     0%\nInfo:                 ALU54B:     0\/   14     0%\nInfo:                EHXPLLL:     0\/    2     0%\nInfo:                EXTREFB:     0\/    1     0%\nInfo:                   DCUA:     0\/    1     0%\nInfo:              PCSCLKDIV:     0\/    2     0%\nInfo:                IOLOGIC:     0\/  128     0%\nInfo:               SIOLOGIC:     0\/   69     0%\nInfo:                    GSR:     0\/    1     0%\nInfo:                  JTAGG:     0\/    1     0%\nInfo:                   OSCG:     0\/    1     0%\nInfo:                  SEDGA:     0\/    1     0%\nInfo:                    DTR:     0\/    1     0%\nInfo:                USRMCLK:     0\/    1     0%\nInfo:                CLKDIVF:     0\/    4     0%\nInfo:              ECLKSYNCB:     0\/   10     0%\nInfo:                DLLDELD:     0\/    8     0%\nInfo:                 DDRDLL:     0\/    4     0%\nInfo:                DQSBUFM:     0\/    8     0%\nInfo:        TRELLIS_ECLKBUF:     0\/    8     0%\nInfo:           ECLKBRIDGECS:     0\/    2     0%\n&#91;...]\necppack --svf taptempo.svf taptempo_out.config taptempo.bit<\/code><\/pre>\n\n\n\n<p>On voit ici que les ressources utilis\u00e9es pour TapTempo sont ridicules par rapport au FPGA&nbsp;utilis\u00e9. La curieuse ou le curieux qui voudra \u00ab\u202fvoir\u202f\u00bb le placement routage dans le FPGA utilisera l\u2019option <code>--gui<\/code> dans la commande NextPnR pour avoir l\u2019interface graphique :<\/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 --gui<\/code><\/pre>\n\n\n\n<p>Ce qui donne un autre aper\u00e7u du remplissage du FPGA.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/img.linuxfr.org\/img\/687474703a2f2f66616269656e6d2e65752f706172746167652f74617074656d706f5f726f7574696e672e706e67\/taptempo_routing.png\" alt=\"taptempo routage\" title=\"Source : http:\/\/fabienm.eu\/partage\/taptempo_routing.png\"\/><\/figure>\n\n\n\n<p>Pour t\u00e9l\u00e9charger le bitstream dans le FPGA, on pourra utiliser <a href=\"https:\/\/github.com\/trabucayre\/openFPGALoader\">openFPGALoader<\/a> en donnant simplement le nom du bitstream :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ openFPGALoader taptempo.bit<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"toc-exercices-de-travaux-pratiques\">Exercices de travaux pratiques<\/h2>\n\n\n\n<p>Pour celles et ceux qui ont suivi jusqu\u2019ici et qui voudraient se faire la main avec ce projet, voici quelques propositions de \u00ab&nbsp;sujet de&nbsp;TP&nbsp;\u00bb\u202f:)&nbsp;:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>utilisation d\u2019un multiplieur c\u00e2bl\u00e9 de l\u2019ECP5 pour faire la division dans&nbsp;per2bpm\u202f;<\/li><li>ajout un module de moyennage sur cinq \u00e9chantillons pour coller \u00e0 la sp\u00e9cification initiale de&nbsp;TapTempo\u202f;<\/li><li>utilisation d\u2019autres plates\u2011formes FPGA \u00e0 bas co\u00fbt&nbsp;: <a href=\"https:\/\/www.crowdsupply.com\/quicklogic\/quickfeather\">QuickFeather<\/a>, <a href=\"http:\/\/www.fabienm.eu\/flf\/fireant-un-petit-nouveau-dans-le-monde-du-fpga-a-bas-cout\/\">FireAnt<\/a>, <a href=\"https:\/\/tangnano.sipeed.com\/en\/\">Tang Nano<\/a>\u2026<\/li><\/ul>\n\n\n\n<p>N\u2019h\u00e9sitez pas \u00e0 me proposer des demandes d\u2019int\u00e9gration Git pour am\u00e9liorer le projet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"toc-conclusion\">Conclusion<\/h2>\n\n\n\n<p>On voit que d\u00e8s que l&rsquo;on passe dans le domaine de l\u2019embarqu\u00e9 les choses se compliquent et prennent plus de temps. Alors que sur un PC on aurait pu faire \u00e7a en <a href=\"https:\/\/linuxfr.org\/users\/zerodeux\/journaux\/taptempo-en-une-ligne\">une&nbsp;ligne de&nbsp;code<\/a>, quand <a href=\"https:\/\/linuxfr.org\/users\/belegar--2\/journaux\/taptempo-sur-stm32f469i-discovery\">on&nbsp;embarque \u00e7a dans un microcontr\u00f4leur<\/a>, c&rsquo;est d\u00e9j\u00e0 plus compliqu\u00e9. Mais si l\u2019on passe dans le monde des FPGA et des ASIC, le projet prend une toute autre dimension. C&rsquo;est la raison pour laquelle il faut toujours se demander si un FPGA est bien \u00e0 propos pour notre projet, non seulement cela co\u00fbtera plus cher en composant qu&rsquo;une solution sur \u00e9tag\u00e8re, mais en plus le temps de d\u00e9veloppement (et donc le co\u00fbt) sera nettement sup\u00e9rieur.<\/p>\n\n\n\n<p>L\u2019id\u00e9e d\u2019utiliser une touche de t\u00e9l\u00e9graphe pour mesurer le tempo n\u2019\u00e9tait peut\u2011\u00eatre pas la meilleure, compte tenu des rebonds qui sont relativement violents. M\u00eame avec le module lisseur de rebond (<a href=\"https:\/\/github.com\/Martoni\/TapTempoASIC\/blob\/master\/hdl\/debounce.v\">debounce<\/a>), il subsiste quelques rebonds trop longs. Un tempo maximum \u00e0&nbsp;250 n\u2019est pas si rapide et l\u2019on est vite frustr\u00e9 de l\u2019atteindre alors qu\u2019on pourrait mesurer des tempos de musiques plus\u2026 rythm\u00e9es. On peut facilement passer \u00e0&nbsp;300, mais \u00e7a reste lent. Si l\u2019on veut un tempo plus rapide, il faut tout d\u2019abord changer la graduation sur le voltm\u00e8tre, puis modifier le param\u00e8tre <code>BPM_MAX<\/code> dans le&nbsp;code.<\/p>\n\n\n\n<p>On a ici un mod\u00e8le de projet qui est facile \u00e0 synth\u00e9tiser sur n\u2019importe quel petit FPGA. C\u2019est un projet qui peut \u00eatre int\u00e9ressant si l\u2019on souhaite se sortir un peu les doigts des LED qui clignotent. La d\u00e9monstration \u00e9tant faite du fonctionnement de l\u2019architecture globale, il est ais\u00e9 de s\u2019en servir pour la r\u00e9\u00e9crire dans d\u2019autres langages de description de mat\u00e9riel comme le VHDL, Chisel (m\u00eame s\u2019il y en a <a href=\"https:\/\/linuxfr.org\/users\/martoni\/journaux\/integration-de-taptempo-chisel-sur-apf27\">d\u00e9j\u00e0&nbsp;une<\/a> pour taptempo), Migen\/Litex, MyHDL, Clash (en&nbsp;plus, \u00e7a permettrait de d\u00e9bloquer <a href=\"https:\/\/linuxfr.org\/redaction\/news\/sortie-de-c-ash-1-0\">la&nbsp;d\u00e9p\u00eache <em>LinuxFr.org<\/em><\/a> sur le&nbsp;sujet)\u2026<\/p>\n\n\n\n<p>Pour le curieux, ou la curieuse, qui sera all\u00e9 voir le code sur le projet GitHub, ce projet a \u00e9t\u00e9 d\u00e9velopp\u00e9 avec une dose de <a href=\"https:\/\/fr.wikipedia.org\/wiki\/M%C3%A9thode_formelle_(informatique)#Preuves_formelles\">preuves&nbsp;formelles<\/a> gr\u00e2ce au logiciel libre <a href=\"http:\/\/www.clifford.at\/papers\/2017\/smtbmc-sby\/slides.pdf\">Yosys-SMTBMC<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Il y a plus de deux ans et demi maintenant, mzf publiait un journal sur le site LinuxFR parlant de son projet \u00abTapTempo\u00bb. L&rsquo;objectif de son programme \u00e9tait simplement de mesurer la cadence d&rsquo;une musique en tapant sur une touche de son clavier, le r\u00e9sultat s&rsquo;affichant simplement dans la console. Ce journal fut le point &hellip; <a href=\"http:\/\/www.fabienm.eu\/flf\/taptempo-en-verilog\/\" class=\"more-link\">Continuer la lecture de <span class=\"screen-reader-text\">TapTempo en Verilog<\/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,1,20],"tags":[188,120,187,30],"class_list":["post-1597","post","type-post","status-publish","format-standard","hentry","category-blog","category-langages","category-non-classe","category-verilog","tag-colorlight","tag-hdl","tag-taptempo","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":"http:\/\/www.fabienm.eu\/flf\/author\/admin\/"},"uagb_comment_info":0,"uagb_excerpt":"Il y a plus de deux ans et demi maintenant, mzf publiait un journal sur le site LinuxFR parlant de son projet \u00abTapTempo\u00bb. L&rsquo;objectif de son programme \u00e9tait simplement de mesurer la cadence d&rsquo;une musique en tapant sur une touche de son clavier, le r\u00e9sultat s&rsquo;affichant simplement dans la console. Ce journal fut le point\u2026","_links":{"self":[{"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/1597","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/comments?post=1597"}],"version-history":[{"count":3,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/1597\/revisions"}],"predecessor-version":[{"id":1600,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/1597\/revisions\/1600"}],"wp:attachment":[{"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/media?parent=1597"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/categories?post=1597"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/tags?post=1597"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}