{"id":710,"date":"2018-05-23T21:54:32","date_gmt":"2018-05-23T20:54:32","guid":{"rendered":"http:\/\/www.fabienm.eu\/flf\/?p=710"},"modified":"2018-05-24T13:50:13","modified_gmt":"2018-05-24T12:50:13","slug":"integration-de-taptempo-chisel-sur-apf27","status":"publish","type":"post","link":"http:\/\/www.fabienm.eu\/flf\/integration-de-taptempo-chisel-sur-apf27\/","title":{"rendered":"Int\u00e9gration de TapTempo-Chisel sur APF27"},"content":{"rendered":"<p>Dans <a href=\"http:\/\/www.fabienm.eu\/flf\/un-composant-electronique-taptempo-avec-chisel3\/\">un premier article<\/a> je d\u00e9crivais le \u00abcore\u00bb de TapTempo en Chisel. Mais si nous souhaitons tester en r\u00e9el il faut choisir une plate-forme sur laquelle le synth\u00e9tiser. Ce choix implique n\u00e9cessairement d&rsquo;ajouter du code pour \u00abpackager\u00bb notre composant.<\/p>\n<p>La carte <a href=\"http:\/\/www.opossom.com\/english\/products-processor_boards-apf27.html\">APF27<\/a> et son <a href=\"http:\/\/www.opossom.com\/english\/products-development_boards-apf27_dev.html\">kit de d\u00e9veloppement<\/a> con\u00e7us par Armadeus Systems sont parfaitement indiqu\u00e9s. En effet la carte poss\u00e8de un FPGA de taille plut\u00f4t raisonnable de chez Xilinx : le <a href=\"https:\/\/www.xilinx.com\/products\/silicon-devices\/fpga\/spartan-3.html\">spartan3A<\/a>. Ce FPGA est coupl\u00e9 \u00e0 un microprocesseur<a href=\"https:\/\/www.nxp.com\/products\/processors-and-microcontrollers\/applications-processors\/i.mx-applications-processors\/i.mx-mature-processors\/multimedia-applications-processors-robust-security-for-mobile-high-performance-connectivity-arm9-core:i.MX27\"> i.MX27<\/a> permettant de communiquer directement via un OS \u00ab\u00e9volu\u00e9\u00bb (ici U-Boot). Et &#8230; comble du perfectionnement, le kit de d\u00e9veloppement est muni d&rsquo;un bouton poussoir, qui nous servira de \u00abtouche tempo\u00bb !<\/p>\n<p>L&rsquo;id\u00e9e est donc d&rsquo;utiliser le bouton du kit pour la tempo et de venir lire le r\u00e9sultat mesur\u00e9 par TapTempoChisel au moyen d&rsquo;une lecture sur <a href=\"http:\/\/www.armadeus.org\/wiki\/index.php?title=IMX27-Spartan3A_interface_description\">le bus de communication du processeur qui est connect\u00e9 au FPGA<\/a>.<\/p>\n<figure id=\"attachment_716\" aria-describedby=\"caption-attachment-716\" style=\"width: 1575px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/taptempoapf27.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-716\" src=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/taptempoapf27.png\" alt=\"Architecture du \u00abpackaging\u00bb de TapTempo\" width=\"1575\" height=\"898\" srcset=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/taptempoapf27.png 1575w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/taptempoapf27-300x171.png 300w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/taptempoapf27-768x438.png 768w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/taptempoapf27-1024x584.png 1024w\" sizes=\"auto, (max-width: 1575px) 100vw, 1575px\" \/><\/a><figcaption id=\"caption-attachment-716\" class=\"wp-caption-text\">Architecture du \u00abpackaging\u00bb de TapTempo<\/figcaption><\/figure>\n<p>On trouvera le code du packaging <a href=\"https:\/\/github.com\/Martoni\/TapTempoChisel\/blob\/master\/src\/main\/scala\/taptempo\/APF27TapTempo.scala\">sur le github du projet<\/a>. L&rsquo;interface du Top est donc relativement simple, et se r\u00e9sume \u00e0 deux signaux :<\/p>\n<ul>\n<li>Le signal d&rsquo;entr\u00e9e (bouton)<\/li>\n<li>Le signal de sortie (data)<\/li>\n<\/ul>\n<p>Cot\u00e9 processeur,\u00a0 il suffira de faire une lecture sur le bus pour pouvoir avoir la valeur en temps r\u00e9el:<\/p>\n<pre>BIOS&gt; md.w C8000000<\/pre>\n<p>Nous verrons plus tard que le design pr\u00e9sent\u00e9 ici est beaucoup trop simpliste et bloque le bus de l&rsquo;apf27 ce qui entra\u00eene une impossibilit\u00e9 de lancer Linux sur la carte.<\/p>\n<p><strong>Les diff\u00e9rents \u00e9l\u00e9ments de notre architecture<\/strong><\/p>\n<p>Tout d&rsquo;abord, pour \u00e9viter au maximum la m\u00e9tastabilit\u00e9, il est n\u00e9cessaire de synchroniser le signal d&rsquo;entr\u00e9e avec l&rsquo;horloge du syst\u00e8me. Pour cela <a href=\"http:\/\/webee.technion.ac.il\/~ran\/papers\/Metastability%20and%20Synchronizers.posted.pdf\">nous devons faire passer le signal bouton par deux bascules D<\/a>.<\/p>\n<figure id=\"attachment_718\" aria-describedby=\"caption-attachment-718\" style=\"width: 1063px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/synchro.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-718\" src=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/synchro.png\" alt=\"synchronization d'un signal externe par deux bascules\" width=\"1063\" height=\"603\" srcset=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/synchro.png 1063w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/synchro-300x170.png 300w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/synchro-768x436.png 768w, http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2018\/05\/synchro-1024x581.png 1024w\" sizes=\"auto, (max-width: 1063px) 100vw, 1063px\" \/><\/a><figcaption id=\"caption-attachment-718\" class=\"wp-caption-text\">synchronisation d&rsquo;un signal externe par deux bascules<\/figcaption><\/figure>\n<p>Pour r\u00e9aliser cela, dans un premier temps nous aurions tendance \u00e0 d\u00e9clarer deux signaux :<\/p>\n<ul>\n<li>Un signal temporaire <strong>tmp<\/strong><\/li>\n<li>le signal synchronis\u00e9 <strong>button_s<\/strong><\/li>\n<\/ul>\n<p>En chisel cela donnerait un truc dans le genre:<\/p>\n<pre>val tmp = RegNext(io.button)\r\nval button_s = RegNext(tmp)\r\n<\/pre>\n<p>On d\u00e9clare le registre en m\u00eame temps que l&rsquo;on connecte sa valeur d&rsquo;entr\u00e9e.<\/p>\n<p>Pourtant \u00e0 y regarder de plus pr\u00e8s, ce montage de la double bascules n&rsquo;est qu&rsquo;un registre \u00e0 d\u00e9calage de 2 ! Et il existe une fonction pour \u00e7a dans la librairie \u00abutil\u00bb de chisel : <strong>ShiftRegister(sig, n)<\/strong><\/p>\n<p>Du coup nous pouvons r\u00e9duire notre synchronisation en une simple ligne :<\/p>\n<pre>val button_s = ShiftRegister(io.button, 2)\r\n<\/pre>\n<p>Notre signal est maintenant synchronis\u00e9, mais nous n&rsquo;avons pas filtr\u00e9 les rebonds. Or avec le genre de boutons que nous trouvons sur ces kits de d\u00e9veloppement c&rsquo;est indispensable. Le FPGA \u00e9tant cadenc\u00e9 \u00e0 une fr\u00e9quence \u00e9lev\u00e9 de 100Mhz nous allons \u00abvoir\u00bb tous les rebonds, et fausser par la m\u00eame occasion notre mesure du tempo.<\/p>\n<p>La plupart des \u00abmontages FPGA\u00bb permettant de faire de l&rsquo;anti-rebond se basent sur des compteurs. Le tout \u00e9tant de bien les dimensionner.<\/p>\n<pre>  val clk_freq_khz = 100000\r\n  val debounce_per_ms = 20\r\n  val MAX_COUNT = (clk_freq_khz * debounce_per_ms) + 1\r\n  val debcounter = RegInit(MAX_COUNT.U)<\/pre>\n<p>La remise \u00e0 z\u00e9ro du compteur sera d\u00e9clench\u00e9e par un front (montant ou descendant) du signal d&rsquo;entr\u00e9e. Nous d\u00e9clarerons pour cela deux fonctions tr\u00e8s commodes:<\/p>\n<pre>def risingedge(x: Bool) = x &amp;&amp; !RegNext(x)\r\ndef fallingedge(x: Bool) = !x &amp;&amp; RegNext(x)<\/pre>\n<p>Permettant de d\u00e9tecter respectivement le front montant et le front descendant du signal d&rsquo;entr\u00e9e.<\/p>\n<p>Tant que le compteur debcounter n&rsquo;a pas atteint sa valeur maximal, on ne fait que compter. Si le compteur est \u00e0 sa valeur max et que l&rsquo;on a un front sur le signal d&rsquo;entr\u00e9e, alors on remet le compteur \u00e0 zero et on recopie la valeur du signal d&rsquo;entr\u00e9e.<\/p>\n<pre>  when(debcounter =\/= MAX_COUNT.U) {\r\n    debcounter := debcounter + 1.U\r\n  }.otherwise {\r\n    when(risingedge(button_s) || fallingedge(button_s)){\r\n      debcounter := 0.U\r\n      button_deb := button_s\r\n    }\r\n}\r\n<\/pre>\n<p>De cette mani\u00e8re on r\u00e9percute rapidement un changement du signal d&rsquo;entr\u00e9e sans s&rsquo;encombrer des multiples changement de valeurs rapide inh\u00e9rentes aux rebonds.<\/p>\n<p><strong>Synth\u00e8se<\/strong><\/p>\n<p>Chisel est \u00abvendu\u00bb \u00e0 la base comme un langage HDL <strong>synth\u00e9tisable<\/strong>, du coup nous allons le synth\u00e9tiser, et avec un logiciel du march\u00e9 s&rsquo;il vous pla\u00eet : ISE.<\/p>\n<p>Avant la synth\u00e8se nous avons besoin du code verilog g\u00e9n\u00e9r\u00e9. Pour le g\u00e9n\u00e9rer nous appellerons le &lsquo;Driver&rsquo; <a href=\"https:\/\/github.com\/Martoni\/TapTempoChisel\/blob\/master\/src\/main\/scala\/taptempo\/APF27TapTempo.scala#L40\">d\u00e9clar\u00e9 dans le top:<\/a><\/p>\n<pre>object APF27TapTempoDriver extends App {\r\n  chisel3.Driver.execute(args, () =&gt; new APF27TapTempo)\r\n}<\/pre>\n<p>Au moyen de la commande sbt :<\/p>\n<pre>sbt 'runMain taptempo.APF27TapTempoDriver'<\/pre>\n<p>Le code verilog ainsi g\u00e9n\u00e9r\u00e9 se retrouve dans le r\u00e9pertoire courant avec le nom <em>APF27TapTempo.v<\/em><\/p>\n<p>Notre projet comportant deux modules verilog (APF27TapTempo et TapTempo) leurs d\u00e9claration dans le fichier source se fait en partant de la fin -&gt; le \u00abtop\u00bb est \u00e0 la fin du fichier et le \u00abcore\u00bb au d\u00e9but:<\/p>\n<pre>...\r\nmodule APF27TapTempo( \/\/ @[:@2517.2]\r\n  input         clock, \/\/ @[:@2518.4]\r\n  input         reset, \/\/ @[:@2519.4]\r\n  output [15:0] io_data, \/\/ @[:@2520.4]\r\n  input         io_button \/\/ @[:@2520.4]\r\n);\r\n...\r\n<\/pre>\n<p>Il ne nous reste plus qu&rsquo;\u00e0 int\u00e9grer ce source \u00e0 un projet ISE en y ajoutant la description des signaux d&rsquo;entr\u00e9es sorties et leurs placement sur les pins du FPGA. Ce qui peut-\u00eatre fait en int\u00e9grant le fichier de description <a href=\"https:\/\/github.com\/Martoni\/TapTempoChisel\/blob\/master\/platforms\/apf27\/APF27TapTempoChisel.ucf\" target=\"_blank\" rel=\"noopener\">APF27TapTempoChisel.ucf<\/a><\/p>\n<pre># clock\r\nNET \"clock\" LOC=\"N9\" | IOSTANDARD=LVCMOS18;# CLK0\r\nNET \"clock\" TNM_NET = \"clock\";\r\nTIMESPEC \"TS_clock\" = PERIOD \"clock\" 10.4167 ns HIGH 50 %;\r\n# data bus\r\nNET \"io_data&lt;0&gt;\"  LOC=\"T5\" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA0\r\nNET \"io_data&lt;1&gt;\"  LOC=\"T6\" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA1\r\nNET \"io_data&lt;2&gt;\"  LOC=\"P7\" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA2\r\nNET \"io_data&lt;3&gt;\"  LOC=\"N8\" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA3\r\nNET \"io_data&lt;4&gt;\"  LOC=\"P12\"| DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA4\r\nNET \"io_data&lt;5&gt;\"  LOC=\"T13\"| DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA5\r\nNET \"io_data&lt;6&gt;\"  LOC=\"R13\"| DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA6\r\nNET \"io_data&lt;7&gt;\"  LOC=\"T14\"| DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA7\r\nNET \"io_data&lt;8&gt;\"  LOC=\"P5\" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA8\r\nNET \"io_data&lt;9&gt;\"  LOC=\"N6\" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA9\r\nNET \"io_data&lt;10&gt;\" LOC=\"T3\" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA10\r\nNET \"io_data&lt;11&gt;\" LOC=\"T11\"| DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA11\r\nNET \"io_data&lt;12&gt;\" LOC=\"T4\" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA12\r\nNET \"io_data&lt;13&gt;\" LOC=\"R5\" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA13\r\nNET \"io_data&lt;14&gt;\" LOC=\"M10\"| DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA14\r\nNET \"io_data&lt;15&gt;\" LOC=\"T10\"| DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA15\r\n# Button\r\nNET \"io_button\" LOC=\"C15\" | DRIVE=12 | IOSTANDARD=LVCMOS33; # IO_L24N_1\r\n<\/pre>\n<p>Et nous pouvons lancer la synth\u00e8se\/placement&amp;routage\/bitstream d&rsquo;ISE. Une fois le bitstream g\u00e9n\u00e9r\u00e9 il faut le transf\u00e9rer dans la m\u00e9moire de l&rsquo;apf27 avec U-Boot :<\/p>\n<pre>BIOS&gt; tftpboot ${loadaddr} APF27TapTempo.bit\r\n<\/pre>\n<p>Puis configurer le FPGA.<\/p>\n<pre>BIOS&gt; fpga load 0 ${loadaddr}\r\n<\/pre>\n<p>Nous pouvons enfin lire la valeur du tempo avec la commande de lecture dans l&rsquo;espace m\u00e9moire du bus fpga (WEIM) :<\/p>\n<pre>BIOS&gt; md.w C8000000\r\nc8000000: 010e 010e 010e 010e 010e 010e 010e 010e ................\r\nc8000010: 010e 010e 010e 010e 010e 010e 010e 010e ................\r\nc8000020: 010e 010e 010e 010e 010e 010e 010e 010e ................<\/pre>\n<p>La valeur est lue en hexad\u00e9cimal. Et comme l&rsquo;adresse n&rsquo;est pas g\u00e9r\u00e9e, tant que \u00e7a reste dans la zone du bus FPGA, la m\u00eame valeur se r\u00e9p\u00e8te.<\/p>\n<p>Ici nous avons donc un tempo de 0x10e soit 270bpm. Pour le calibrer, j&rsquo;ai pris le chronom\u00e8tre et tent\u00e9 d&rsquo;appuyer sur le bouton toutes les secondes, ce qui doit logiquement donner 60bpm -&gt; 0x3c.<\/p>\n<p>Nous n&rsquo;en somme pas trop loin :<br \/>\n<a href=\"https:\/\/youtu.be\/pNQnmBw5iL4\">Test de la calibration de TapTempoChisel sur APF27<\/a><br \/>\n<iframe loading=\"lazy\" src=\"https:\/\/www.youtube.com\/embed\/pNQnmBw5iL4\" width=\"560\" height=\"315\" frameborder=\"0\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<p>Il est d\u00e9sormais possible de l&rsquo;utiliser dans le cas concret de la mesure du tempo du tr\u00e8s mauvais \u00abnuit de folie\u00bb du groupe \u00abd\u00e9but de soir\u00e9e\u00bb .<\/p>\n<p>Mesure du tempo du tr\u00e8s mauvais \u00abnuit de folie\u00bb :<br \/>\n<iframe loading=\"lazy\" src=\"https:\/\/www.youtube.com\/embed\/j-Zf7-ank1U\" width=\"560\" height=\"315\" frameborder=\"0\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<p>On obtient une valeur de 0x7B soit 123 coups par minute (bpm).<\/p>\n<p>Ps: si vous voulez laver votre cerveau de cette horrible chanson pourquoi pas une petite <a href=\"https:\/\/www.youtube.com\/watch?time_continue=16&amp;v=_6wyJuLGKUU\">gu\u00e9rilla<\/a> ? \u00c0 moins que vous soyez adepte du <a href=\"https:\/\/www.youtube.com\/watch?v=mTYTm6X8yw8\">crou<\/a>. Ne me remerciez pas, moi aussi j&rsquo;ai beaucoup souffert \u00e0 mesurer le tempo de cette horreur \ud83d\ude09<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dans un premier article je d\u00e9crivais le \u00abcore\u00bb de TapTempo en Chisel. Mais si nous souhaitons tester en r\u00e9el il faut choisir une plate-forme sur laquelle le synth\u00e9tiser. Ce choix implique n\u00e9cessairement d&rsquo;ajouter du code pour \u00abpackager\u00bb notre composant. La carte APF27 et son kit de d\u00e9veloppement con\u00e7us par Armadeus Systems sont parfaitement indiqu\u00e9s. En &hellip; <a href=\"http:\/\/www.fabienm.eu\/flf\/integration-de-taptempo-chisel-sur-apf27\/\" class=\"more-link\">Continuer la lecture de <span class=\"screen-reader-text\">Int\u00e9gration de TapTempo-Chisel sur APF27<\/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":[4,3,24],"tags":[],"class_list":["post-710","post","type-post","status-publish","format-standard","hentry","category-chisel-langages","category-langages","category-materiel"],"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":1,"uagb_excerpt":"Dans un premier article je d\u00e9crivais le \u00abcore\u00bb de TapTempo en Chisel. Mais si nous souhaitons tester en r\u00e9el il faut choisir une plate-forme sur laquelle le synth\u00e9tiser. Ce choix implique n\u00e9cessairement d&rsquo;ajouter du code pour \u00abpackager\u00bb notre composant. La carte APF27 et son kit de d\u00e9veloppement con\u00e7us par Armadeus Systems sont parfaitement indiqu\u00e9s. En\u2026","_links":{"self":[{"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/710","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=710"}],"version-history":[{"count":14,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/710\/revisions"}],"predecessor-version":[{"id":733,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/710\/revisions\/733"}],"wp:attachment":[{"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/media?parent=710"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/categories?post=710"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/tags?post=710"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}