Migen, une « boîte à outils » en Python pour concevoir des circuits logiques complexes

Magazine
Marque
GNU/Linux Magazine
Numéro
149
|
Mois de parution
mai 2012
|
Domaines


Résumé

La conception de circuits logiques en VHDL ou Verilog est souvent perçue comme rébarbative. Les débutants sont régulièrement perdus dans la masse de détails à maîtriser en une seule fois pour obtenir un circuit fonctionnel. Les utilisateurs expérimentés pourraient être plus productifs en automatisant certaines tâches au lieu d'éditer trop souvent le code bas niveau à la main. Cet article donne une (courte) introduction à Migen, une bibliothèque Python permettant de manipuler et générer plus facilement des circuits logiques complexes.


Body

1. Installation de Migen

 

migen_logo

 

Logo Migen (CC-BY-SA)

Obtenez tout d'abord les sources depuis le dépôt Git. Migen est encore nouveau et expérimental, et il n'existe pas de « version stable ».

git clone git://github.com/milkymist/migen.git

cd migen

Migen est prévu pour fonctionner avec Python 3.2. Pour installer les fichiers de façon permanente sur votre système, lancez le script setup.py fourni :

python3 setup.py

Vous pouvez aussi tout simplement utiliser la variable d'environnement PYTHONPATH à la place :

export PYTHONPATH=`pwd`

Pour pouvoir simuler votre circuit, vous aurez besoin d'Icarus Verilog. Cependant, il n'est pas encore possible d'utiliser les paquets de votre distribution car un bug dans l'API vpi_put_value() d'Icarus Verilog empêche le bon fonctionnement des écritures de signaux depuis Python. Vous devrez donc patcher et recompiler Icarus Verilog manuellement :

git clone git://github.com/steveicarus/iverilog.git

cd iverilog

patch -p1 < 0001-Copy-vector-values-in-vpi_put_value.patch

./configure && make && make install

Le fichier patch se trouve dans le répertoire vpi des sources de Migen [NDLA : Update, le patch est upstream, il suffit maintenant de compiler Icarus à partir des sources Git].

Compilez et installez ensuite le module permettant la communication entre Migen et Icarus Verilog :

cd migen/vpi

make && make install

Bien évidemment, si vous êtes maintainer dans une distribution, il ne tient qu'à vous de rendre ces étapes plus simples pour tout le monde ;)

2. Modèle de circuit

Après ces considérations d'ordre pratique, intéressons-nous à la façon dont les circuits sont modélisés par Migen. Il n'est possible ici de modéliser que des circuits logiques synchrones, qui représentent la quasi-totalité de la logique présente sur une puce numérique (et en particulier d'un FPGA) de nos jours.

Dans notre modèle, un circuit est composé de deux parties :

- une liste d'instructions combinatoires, qui sont évaluées à chaque changement de leurs entrées.

- une liste d'instructions synchrones, qui sont évaluées à chaque cycle d'horloge.

Les instructions sont décrites par une structure d'objets Python faisant référence à des signaux - ces derniers représentant des points de connexion nommés dans le circuit.

Par exemple, voici comment représenter une porte AND ayant les signaux a et b pour entrée, et le signal x pour sortie :

a = Signal()

b = Signal()

x = Signal()

and = _Assign(x, _Operator('&', [a, b]))

La syntaxe de cette dernière ligne étant particulièrement lourde, Migen redéfinit les opérateurs Python pour les signaux et fournit plusieurs méthodes et fonctions permettant une syntaxe plus lisible. Il s'agit de la technique « internal domain specific language » assez à la mode chez les Rubyistes. On peut ainsi réécrire cette ligne de cette façon :

and = x.eq(a & b)

Les listes d'instructions combinatoires et synchrones sont ensuite encapsulées dans un fragment, qui peut ensuite être combiné avec d'autres, simulé, ou converti en Verilog synthétisable pour une implémentation matérielle.

3. Exemple pratique

3.1. Description du circuit

Pour débuter, nous allons réaliser un simple diviseur de fréquence générant un sous-multiple de l'horloge système. Ce circuit a l'avantage de pouvoir faire clignoter une LED sur une carte FPGA, et de tester ainsi la technique d'implémentation matérielle avec un exemple trivial.

Notre diviseur consiste en un décompteur synchrone qui, lorsqu'il atteint 0, provoque l'inversion du signal de sortie. Cela permet d'obtenir un rapport cyclique de 50% et donc un clignotement visible de la LED.

Nous encapsulons également notre code dans une classe Python. Même si cela n'apporte pas d'avantage clair ici, cela permet de démontrer cette possibilité qui peut être fort utile avec certaines architectures.

from migen.fhdl.structure import *

class FreqDivider:

 def __init__(self, ratio):

  self.out = Signal()

  self.ratio = ratio

 

 def get_fragment(self):

  r = self.ratio//2 - 1

  counter = Signal(BV(bits_for(r)))

  sync = [

   If(counter == 0,

    self.out.eq(~self.out),

    counter.eq(r)

   ).Else(

    counter.eq(counter - 1)

   )

  ]

  return Fragment(sync=sync)

Remarquez qu'à l'initialisation du circuit, tous les signaux ont par défaut la valeur 0 (Migen insère automatiquement le code Verilog pour cela).

3.2. Simulation

Créons une instance de notre diviseur avec un faible rapport de fréquence (afin de pouvoir observer facilement le fonctionnement sur un chronogramme) et obtenons le fragment correspondant :

my_divider = FreqDivider(8)

my_fragment = my_divider.get_fragment()

Importons ensuite les modules de simulation, et exécutons une simulation pour 20 cycles :

from migen.sim.generic import Simulator, TopLevel

from migen.sim.icarus import Runner

sim = Simulator(my_fragment, Runner(), TopLevel("my.vcd"))

sim.run(20)

Ouvrez ensuite le fichier my.vcd avec GTKWave, Dinotrace ou un équivalent pour observer le chronogramme :

 

chrono_divider

 

Fig. 1 : Chronogramme de notre diviseur de fréquence

Attention : le fichier VCD peut être corrompu si le simulateur est encore en attente. Pour éviter cela, terminez l'interpréteur Python, ou détruisez uniquement l'objet avec :

del sim

Remarquez que Migen génère automatiquement l'horloge système ainsi qu'un signal de reset.

Cet exemple très simple ne fait qu'effleurer les possibilités du simulateur. Il est notamment possible d'exécuter, pendant la simulation, du code Python qui manipule les signaux afin d'effectuer des tests complexes et puissants. Des exemples et une documentation plus complète (en anglais) sont fournis avec le code source de Migen. Pour vous donner un aperçu, voici un extrait de wb_initiator.py qui utilise les générateurs Python pour exécuter des transactions sur un bus Wishbone avec un style de code séquentiel :

def my_generator():

 prng = Random(92837)

 # Ecritures

 for x in range(10):

  t = TWrite(x, 2*x)

  yield t

  print("Ecriture en " + str(t.latency) + " cycle(s)")

  # Insère des cycles morts pour simuler l'inactivité sur le bus

  for delay in range(prng.randrange(0, 3)):

   yield None

 # Lectures

 for x in range(10):

  t = TRead(x)

  yield t

  print("Lecture de " + str(t.data) + " en " + str(t.latency) + " cycle(s)")

  for delay in range(prng.randrange(0, 3)):

   yield None

3.3. Implémentation matérielle

Nous allons maintenant utiliser notre diviseur de fréquence pour faire clignoter une LED sur une carte FPGA. Tout d'abord, créez un nouveau diviseur avec un rapport plus raisonnable pour cela :

led_divider = FreqDivider(50000000)

led_fragment = led_divider.get_fragment()

Vous pouvez ensuite transcrire le fragment correspondant en Verilog :

from migen.fhdl import verilog

verilog_source = verilog.convert(led_fragment, ios={led_divider.out})

print(verilog_source)

Le paramètre ios donne l'ensemble des signaux qui doivent devenir des ports dans le code généré. Par défaut, les ports sys_clk et sys_rst pour l'horloge et le reset sont implicites. Cela donne le résultat suivant :

/* Machine-generated using Migen */

module top(

        input sys_rst,

        output reg out,

        input sys_clk

);

reg [24:0] counter;

always @(posedge sys_clk) begin

        if (sys_rst) begin

                out <= 1'd0;

                counter <= 25'd0;

        end else begin

                if ((counter == 1'd0)) begin

                        out <= (~out);

                        counter <= 25'd24999999;

                end else begin

                        counter <= (counter - 1'd1);

                end

        end

end

endmodule

Vous pouvez utiliser ce code source avec vos outils FPGA préférés (ou détestés) pour réaliser l'essai sur carte de développement. Le dépôt Git milkymist-ng donne des exemples de scripts pour les outils Xilinx et la plate-forme Milkymist One.

4. Pour aller plus loin

Outre le fait de pouvoir exécuter du code Python lors de la simulation, le point fort de Migen réside dans la possibilité de générer facilement des circuits depuis Python, en construisant des listes d'instructions synchrones et combinatoires. Il est ainsi possible d'utiliser les objets, les fonctions, la métaprogrammation, etc., pour organiser le code, réduire la redondance, et le rentre paramétrable (et donc plus réutilisable).

Pour illustrer cela, Migen dispose notamment d'un module permettant de réaliser automatiquement des bancs de registres de configuration et d'état à partir d'une description abstraite. Par exemple, pour concevoir un SoC ou un microcontrôleur, les emplacements mémoire configurant la vitesse de transmission d'une UART et contenant les caractères transférés seront simplement listés avec le comportement voulu pour chaque registre (lecture seule depuis le bus, signaux de contrôle voulus côté UART, etc.). Cette liste générique sera lue par un module qui produira la logique implémentant l'interface de bus choisie (Wishbone, CSR-2, ...). Il est également possible de construire les contrôleurs d'interruption de cette manière.

L'interconnexion de composants sur un bus et l'arbitration de ce bus sont également très simples grâce aux composants de la bibliothèque. Par exemple, pour connecter plusieurs maîtres Wishbone à plusieurs esclaves par l'intermédiaire d'un bus partagé, il suffit d'écrire ceci (il est bien sûr possible de contrôler les adresses des esclaves avec des paramètres supplémentaires) :

wishbonecon0 = wishbone.InterconnectShared(

 [

  cpu0.ibus,

  cpu0.dbus

 ], [

  norflash0.bus,

  sram0.bus,

  wishbone2asmi0.wishbone,

  wishbone2csr0.wishbone

 

Enfin (mais cela n'est pas encore très développé pour le moment), il sera possible de réaliser des accélérateurs matériels assez simplement en utilisant le paradigme « dataflow », peut-être avec un langage tel que RVC-CAL.

5. Domaines d'application

Migen (Milkymist generator) va devenir la base de la prochaine version majeure du system-on-chip libre Milkymist. Si vous avez une Milkymist One, vous pouvez d'ores et déjà essayer la version de développement actuelle :

git clone git://github.com/milkymist/milkymist-ng.git

cd milkymist-ng

cd software/bios

make # nécessite Clang/LLVM avec patch

make flash # écriture du nouveau BIOS via UrJTAG et m1nor

cd ../..

make # nécessite Xilinx ISE

make load # chargement du nouveau bitstream via UrJTAG

Le nouveau BIOS est compilé avec Clang et LLVM. Le support du processeur LM32 utilisé dans Milkymist n'étant pas encore inclus dans les versions officielles, vous aurez besoin de compiler ces logiciels à partir de nos dépôts Git (merci à JP Bonn pour ce travail) :

git clone git://github.com/milkymist/llvm-lm32.git

cd llvm-lm32/tools

git clone git://github.com/milkymist/clang-lm32.git clang

cd ../..

mkdir build && cd build

cmake .. && make && make install

Un autre domaine d'application de Migen est la plate-forme de radio logicielle Rhino, développée à l'université du Cap, en Afrique du Sud. Migen est utilisé pour automatiser la construction d'une « application Rhino », c'est-à-dire un ensemble de composants (chaînes de traitement numérique du signal, interfaces avec les ADC/DAC, ...) chargés dans le FPGA et communiquant avec le logiciel fonctionnant sur un SoC externe faisant tourner Linux. Il est également envisagé d'utiliser les futures fonctionnalités « dataflow » de Migen pour réaliser les traitements sur les signaux, notamment dans des systèmes d'imagerie radar sophistiqués.

 

rhino_block_diagram_v1

 

Fig. 2 : Schéma-bloc de la plate-forme Rhino (CC-BY-SA Alan Langman, Simon Scott)

 

rhino_board

 

Fig. 3 : Photographie d'un système Rhino (CC-BY-SA Alan Langman)

Si vous avez des questions, n'hésitez pas à venir les poser sur IRC (canal #milkymist sur le réseau Freenode) ou via la liste devel@lists.milkymist.org. À bientôt !

 

Sur le même sujet

Faites du multithreading avec OpenMP

Magazine
Marque
GNU/Linux Magazine
Numéro
237
|
Mois de parution
mai 2020
|
Domaines
Résumé

Vous vous êtes souvent demandé s’il était possible de paralléliser certaines tâches de votre application afin de la rendre plus optimale en termes de temps ? Grâce à OpenMP, vous pourrez optimiser la vitesse d’exécution de vos programmes en parallélisant les tâches facilement.

Erlang, programmation distribuée et modèle acteur

Magazine
Marque
GNU/Linux Magazine
Numéro
237
|
Mois de parution
mai 2020
|
Domaines
Résumé

Quel est le point commun entre RabbitMQ, ejabberd, CouchDB, WhatsApp et Heroku ? Ces outils et services ont la particularité d’utiliser le même langage de programmation : Erlang. Ce dernier, encore trop peu connu du grand public, a pourtant réussi à maintes reprises à faire parler de lui. Il offre une approche nouvelle dans le monde du développement, où le paradigme orienté objet domine largement le marché, en offrant une implémentation très haut niveau du modèle acteur, facilitant ainsi la mise en place ainsi que l’utilisation de systèmes complexes et distribués.

Programmation réactive avec Quarkus

Magazine
Marque
GNU/Linux Magazine
Numéro
237
|
Mois de parution
mai 2020
|
Domaines
Résumé

L’innovant projet Quarkus a déjà été évoqué dans plusieurs précédents articles depuis sa sortie en mai 2019. Ces publications ont détaillé à quel point il est un excellent cadre d’exécution, très performant et très adapté à la réalisation de microservices HTTP. Néanmoins, ces différents articles n’ont pas, pour le moment, abordé le thème de la programmation « réactive ». Or, comme nous allons le voir aujourd’hui, la programmation « réactive » est en fait au cœur de Quarkus !

Par le même auteur

Migen, une « boîte à outils » en Python pour concevoir des circuits logiques complexes

Magazine
Marque
GNU/Linux Magazine
Numéro
149
|
Mois de parution
mai 2012
|
Domaines
Résumé

La conception de circuits logiques en VHDL ou Verilog est souvent perçue comme rébarbative. Les débutants sont régulièrement perdus dans la masse de détails à maîtriser en une seule fois pour obtenir un circuit fonctionnel. Les utilisateurs expérimentés pourraient être plus productifs en automatisant certaines tâches au lieu d'éditer trop souvent le code bas niveau à la main. Cet article donne une (courte) introduction à Migen, une bibliothèque Python permettant de manipuler et générer plus facilement des circuits logiques complexes.

Implémentation efficace d'algorithmes sur FPGA : Machines à état

Magazine
Marque
Open Silicium
Numéro
2
|
Mois de parution
avril 2011
|
Domaines
Résumé

L'article précédent vous a présenté deux façons de réaliser un multiplieur complexe, toutes deux proches de la méthode naïve qui consiste à utiliser un circuit arithmétique de base (multiplieur, additionneur, soustracteur) par opération et à les connecter selon l'arbre de calcul. Nous allons maintenant voir comment partager le même circuit arithmétique entre plusieurs opérations.

Implémentation efficace d'algorithmes sur FPGA

Magazine
Marque
Open Silicium
Numéro
1
|
Mois de parution
janvier 2011
|
Domaines
Résumé

Les FPGA ouvrent des possibilités de calculs extrêmement performants, à condition de bien savoir les utiliser. Cet article est le premier d'une série visant à montrer, en partant d'un exemple simple, comment différents choix d'architectures impactent les performances et l'utilisation des ressources disponibles sur le FPGA. Ce choix est primordial mais souvent négligé dans beaucoup de « cores » open source qui sont parfois des monstres de lenteur et d'inefficacité.

SoC Milkymist: le développement logiciel en pratique

Magazine
Marque
GNU/Linux Magazine
Numéro
130
|
Mois de parution
septembre 2010
|
Résumé
Le numéro 124 de février vous a donné une présentation générale et assez théorique du System-on-Chip (SoC) libre Milkymist. Maintenant, nous allons nous orienter vers la pratique en détaillant les différentes opérations nécessaires au développement de logiciels fonctionnant sur cette plate-forme : installation des outils de compilation, construction d'un noyau Linux compatible, compilation d'applications et utilisation de l'émulateur QEMU, pour finir avec la configuration d'une carte de développement FPGA permettant de prototyper le SoC et le déploiement du logiciel sur cette dernière.

Milkymist, un System-on-Chip libre et orienté vidéo temps réel

Magazine
Marque
GNU/Linux Magazine
Numéro
124
|
Mois de parution
février 2010
|
Résumé
Jusqu'à récemment, la conception de puces et en particulier de System-on-Chips (puce embarquant à la fois un microprocesseur performant et de nombreux périphériques) était réservée aux laboratoires privés et à la recherche académique, pour des raisons principalement financières. Cela a exclu le monde du libre - reposant largement sur les « bidouilleurs » - de ce milieu, et ainsi, l'écrasante majorité des System-on-Chips vendus actuellement sont issus de documents de conception propriétaires et jalousement gardés. Aujourd'hui, la large disponibilité de FPGA (puces « sculptables » à volonté) relativement performants et à faible coût a le potentiel de changer la donne. Cet article donne une présentation générale de Milkymist, un System-on-Chip sous GPL capable de faire fonctionner Linux et destiné à des applications de performances artistiques vidéo en temps réel telles que le VJ.