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
Spécialité(s)


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 !

 



Article rédigé par

Par le(s) même(s) auteur(s)

Implémentation efficace d'algorithmes sur FPGA

Magazine
Marque
Open Silicium
Numéro
1
Mois de parution
janvier 2011
Spécialité(s)
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.

Les derniers articles Premiums

Les derniers articles Premium

Game & Watch : utilisons judicieusement la mémoire

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Au terme de l'article précédent [1] concernant la transformation de la console Nintendo Game & Watch en plateforme de développement, nous nous sommes heurtés à un problème : les 128 Ko de flash intégrés au microcontrôleur STM32 sont une ressource précieuse, car en quantité réduite. Mais heureusement pour nous, le STM32H7B0 dispose d'une mémoire vive de taille conséquente (~ 1,2 Mo) et se trouve être connecté à une flash externe QSPI offrant autant d'espace. Pour pouvoir développer des codes plus étoffés, nous devons apprendre à utiliser ces deux ressources.

Raspberry Pi Pico : PIO, DMA et mémoire flash

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Le microcontrôleur RP2040 équipant la Pico est une petite merveille et malgré l'absence de connectivité wifi ou Bluetooth, l'étendue des fonctionnalités intégrées reste très impressionnante. Nous avons abordé le sujet du sous-système PIO dans un précédent article [1], mais celui-ci n'était qu'une découverte de la fonctionnalité. Il est temps à présent de pousser plus loin nos expérimentations en mêlant plusieurs ressources à notre disposition : PIO, DMA et accès à la flash QSPI.

Programmation des PIO de la Raspberry Pi Pico

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

La carte Pico de Raspberry Pi est appréciable à bien des égards. Ses ressources, son prix, ses deux cœurs ARM... Mais ce morceau de silicium qu'est le RP2040 renferme une fonctionnalité unique : des blocs PIO permettant de créer librement des périphériques supplémentaires qu'il s'agisse d'éléments standardisés comme SPI, UART ou i2c, ou des choses totalement exotiques et très spécifiques à un projet ou un environnement donné. Voyons ensemble comment prendre en main cette ressource et explorer le monde fantastique des huit machines à états de la Pico !

Les listes de lecture

9 article(s) - ajoutée le 01/07/2020
Vous désirez apprendre le langage Python, mais ne savez pas trop par où commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en découvrant l'écosystème de Python et en écrivant de petits scripts.
11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tâche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.
10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de posséder des pétaoctets de données si l'on est incapable d'analyser ces dernières ? Cette liste vous aidera à "faire parler" vos données.
Voir les 76 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous