URL: https://linuxfr.org/users/martoni/journaux/un-composant-electronique-taptempo-avec-chisel3 Title: Un composant électronique TapTempo avec Chisel3 Authors: martoni Date: 2018年04月15日T21:18:30+02:00 License: CC By-SA Tags: hdl, sshdl, verilog, scala, taptempo et chisel Score: 57 Le [«défi» TapTempo](https://duckduckgo.com/?q=TapTempo+site%3Alinuxfr.org&ia=web) est un peu en train de faiblir je trouve. Du coup je vous propose un nouveau langage pour réaliser TapTempo : [Chisel](https://chisel.eecs.berkeley.edu/). Et pour être plus précis, la version 3 de Chisel. Contrairement à tous les langages proposés jusqu'ici, Chisel ne permet pas de réaliser un programme qui sera exécuté par une machine réelle ou virtuelle. Chisel permet de décrire l'architecture de la machine elle-même ! C'est ce qu'on appelle un langage de description matériel ou HDL pour Hardware Description Langage. Ce sont des langages utilisés pour décrire les «IP» pour [les FPGA, les CPLD](https://fr.wikipedia.org/wiki/Circuit_logique_programmable) mais aussi (et surtout en fait) les [ASIC](https://fr.wikipedia.org/wiki/Application-specific_integrated_circuit). Les deux HDL les plus connus sont le VHDL et le Verilog. Cependant, ce sont de vieux langages extrêmement verbeux et pas très modulaires. Un design correct en simulation ne le sera pas forcément en synthèse. C'est pourquoi plusieurs nouveaux langages de description de matériel sont en train de voir le jour aujourd'hui. Chisel est l'un d'eux. Lancé initialement par l'université de Berkley, [Chisel](https://chisel.eecs.berkeley.edu/) est basé sur le langage Scala. Il permet de faire une description synchrone et synthétisable de son design. La même université utilise Chisel pour faire des «core» [Risc-V.](https://riscv.org/) Pour pouvoir être compatible avec la plupart des logiciels de synthèse du marché les IP écrites en Chisel sont ensuite converties en (en passant par un langage «netlist» intermédiaire nommé FIRRTL) Verilog, qui lui sera synthétisable et simulable avec tous les logiciels connus. Dans cette «IP» TapTempo nous laisserons de côté la partie synchronisation du signal externe ainsi que la gestion des rebonds du bouton qui sont propres à l'intégration. Nous nous concentrerons sur l'essence de la fonctionnalité. ![Vue «externe» de TapTempo](http://www.fabienm.eu/flf/wp-content/uploads/2018/03/general-1.jpg) Plutôt que de taper une touche, l'idée ici est d'utiliser un bouton. Le résultat se présente ensuite sous la forme d'un signal binaire sur 9 bits. D'après la page wikipedia des battements par minute, 270bpm est déjà très rapide, pas la peine d'aller plus loin dans le dimensionnement. Par conséquent 9 bits suffisent pour présenter le résultat (entier positif). ![Architecture de TapTempo](http://www.fabienm.eu/flf/wp-content/uploads/2018/03/Struct-2.jpg) Le schéma d'architecture de TapTempo est un peu «à main levée» mais ça permet de comprendre l'esprit du composant. Un générateur de «ticks» génère des ticks à une fréquence de 1kHz. Ces ticks sont comptés par le compteur «count». À chaque appui sur le bouton le résultat du compteur est stocké dans un registre «countx», le pointeur «mux» est incrémenté et le compteur est remis à zéro. On choisira une architecture avec seulement **4** registres d'échantillons «countx» car il est incroyablement plus facile de faire une division par 4 (un simple décalage à droite de deux) que si nous avions eu 5 valeurs comme pour les autres programmes. Les 4 valeurs «countx» sont donc additionnées puis divisées par 4 et vient le problème de la division (inversion) permettant de convertir une période moyenne en une fréquence. Il est très compliqué de diviser dans un FPGA. On emprunte donc généralement des chemins détournés pour arriver à nos fins. Ici nous allons créer une table de registres avec toutes les valeurs «en dur» pré-calculées. Puis nous comparerons (inégalité) la valeur du compteur à tous les registres de manière parallèle (toutes les comparaisons se font en même temps), le résultat sera un vecteur de 270 bits rempli de '1' jusqu'à la valeur voulue. Pour avoir une sortie sur 9 bits et non sur 270 (ça fait vraiment trop de LED à souder pour la maquette !) Chisel fournit une petite fonction bien utile permettant de sortir la valeur de l'index du bit de poids faible à '1' : le PriorityEncoder. Comme nous on veut le poids fort on retournera le vecteur et on fera une petite soustraction de 270. Voila pour le paquet d'explications. Et voila pour le code : ```scala package taptempo import chisel3._ import chisel3.util.{Counter, PriorityEncoder, Reverse} import scala.language.reflectiveCalls //avoid reflective call warnings import scala.math.pow // default clock 100Mhz -> T = 10ns class TapTempo(tclk_ns: Int, bpm_max: Int = 270) extends Module { val io = IO(new Bundle { val bpm = Output(UInt(9.W)) val button = Input(Bool()) }) /* Constant parameters */ val MINUTE_NS = 60*1000*1000*1000L val PULSE_NS = 1000*1000 val TCLK_NS = tclk_ns val BPM_MAX = bpm_max /* usefull function */ def risingedge(x: Bool) = x && !RegNext(x) val tp_count = RegInit(0.asUInt(16.W)) val (pulsecount, timepulse) = Counter(true.B, PULSE_NS/tclk_ns) val countx = RegInit(Vec(Seq.fill(4)(0.asUInt(19.W)))) val count_mux = RegInit(0.asUInt(2.W)) val sum = Wire(UInt(19.W)) /* div array calculation */ val x = Seq.tabulate(pow(2,16).toInt-1)(n => ((MINUTE_NS/PULSE_NS)/(n+1)).U) val bpm_calc = RegInit(Vec(x(0) +: Seq.tabulate(bpm_max)(n => x(n)))) val bpm_ineq = RegInit(Vec(Seq.fill(270)(0.asUInt(1.W)))) when(timepulse) { tp_count := tp_count + 1.U } when(risingedge(io.button)){ countx(count_mux) := tp_count count_mux := Mux(count_mux === 3.U, 0.U, count_mux + 1.U) tp_count := 0.U } sum := countx(0) + countx(1) + countx(2) + countx(3) val sum_by_4 = sum(18, 2) for(i <- 0 to (bpm_max-1)) { bpm_ineq(i) := Mux(sum_by_4 < bpm_calc(i), 1.U, 0.U) } io.bpm := bpm_max.U - PriorityEncoder(Reverse(bpm_ineq.asUInt())) } ``` La totalité du code, des outils de simulation et de visualisation sont disponibles sur un [dépôt github](https://github.com/Martoni/TapTempoChisel) et accessible depuis la TapTempo Fédération. Il est possible de simuler le composant avec la commande sbt suivante: ```sh $ sbt 'test:runMain taptempo.TapTempoMain --backend-name verilator' ``` Un jour je ferais un vrai journal sur Chisel, mais là c'était surtout histoire d'être dans la course à TapTempo ;)

AltStyle によって変換されたページ (->オリジナル) /