Créer et programmer rapidement des conceptions FPGA avec Python et des notebooks Jupyter

Par Stephen Evanczuk

Avec la contribution de Rédacteurs nord-américains de DigiKey

Les concepteurs ont toujours eu recours aux réseaux de portes programmables par l'utilisateur (FPGA) pour accélérer les performances des conceptions matérielles pour des applications à calcul intensif, comme la vision par ordinateur, les communications et les systèmes embarqués industriels, et de plus en plus pour l'Internet des objets (IoT). Toutefois, les étapes détaillées de la programmation FPGA conventionnelle ont toujours été rédhibitoires, ce qui a encouragé les concepteurs à chercher des solutions de traitement alternatives, jusqu'à aujourd'hui.

L'émergence de l'environnement de développement Python Productivity for Zynq (PYNQ) basé sur des notebooks Jupyter permet de résoudre les problèmes de programmabilité FPGA. Grâce à une carte de développement spécialement conçue pour prendre en charge PYNQ, les développeurs avec peu d'expérience FPGA peuvent implémenter rapidement des conceptions capables de tirer pleinement parti des performances des FPGA pour accélérer les applications impliquant des tâches de calcul intensives.

Cet article décrit d'abord l'approche FPGA typique avant d'expliquer comment commencer à utiliser une carte de développement de Digilent qui fournit une alternative open-source puissante pour accélérer le développement de systèmes basés sur FPGA.

Pourquoi des FPGA ?

Les ingénieurs qui ont besoin d'utiliser des algorithmes complexes impliquant des tâches de calcul intensives ont souvent recours à des FPGA pour accélérer l'exécution sans compromettre les budgets d'alimentation serrés. En fait, les FPGA sont devenus une plateforme dominante pour accélérer les algorithmes d'intelligence artificielle dans les systèmes Edge Computing (voir l'article relatif à l'utilisation de FPGA pour créer des applications de vision embarquées hautes performances avec l'apprentissage machine).

Conçus spécialement pour les applications embarquées, les systèmes sur puce (SoC) FPGA plus avancés intègrent une structure logique programmable avec un microcontrôleur. Par exemple, le SoC Zynq-7000 de Xilinx combine un processeur double cœur Arm® Cortex®-A9 avec jusqu'à 444 000 cellules logiques dans sa structure logique programmable intégrée (Figure 1). Avec ses processeurs intégrés et un large éventail de périphériques, le SoC Zynq offre jusqu'à 2020 blocs de traitement numérique des signaux (DSP) ou tranches. Grâce à ces ressources, les développeurs peuvent configurer la structure logique programmable en chaînes de traitement spécialisées, requises pour accélérer le débit dans les algorithmes à calcul intensif complexes.

Schéma du SoC Zynq-7000 de Xilinx

Figure 1 : Le système sur puce Zynq-7000 de Xilinx combine un processeur double cœur Arm Cortex-A9, une structure logique programmable et un large éventail de périphériques et d'interfaces nécessaires dans de nombreuses applications embarquées. (Source de l'image : Xilinx)

En plus de la réduction du nombre de pièces, l'intégration des processeurs et de la structure logique programmable permet l'exécution d'opérations sur des bus sur puce plutôt que par l'intermédiaire d'un accès hors puce. Cette intégration simplifie davantage la tâche essentielle de chargement de la structure logique programmable pendant les séquences de mise sous tension ou de réinitialisation.

Dans un système typique basé sur un microcontrôleur avec FPGA, les développeurs devaient auparavant gérer la séquence et la sécurité pour le chargement de trains de bits permettant de programmer le FPGA. Avec le système sur puce Zynq, un processeur intégré exécute les tâches d'un microcontrôleur traditionnel, notamment la gestion de la structure logique programmable et d'autres périphériques embarqués. Le processus de chargement FPGA ressemble donc davantage à celui d'un processus de démarrage de microcontrôleur traditionnel qu'à une initialisation de train de bits de FPGA.

Ce processus de démarrage se produit pendant une courte séquence d'étapes gérées par l'un des processeurs du Zynq (Figure 2). Lors d'une mise sous tension ou d'une réinitialisation, le processus de démarrage commence lorsqu'un processeur Zynq exécute un extrait de code provenant de sa mémoire BootROM en lecture seule pour récupérer le code de démarrage réel à partir d'un dispositif de démarrage. Outre le code pour configurer les composants système du processeur, le code de démarrage inclut le train de bits de la logique programmable ainsi que l'application utilisateur. Une fois le chargement du code de démarrage terminé, le processeur utilise le train de bits inclus pour configurer la logique programmable. Une fois que la configuration générale et la configuration de la logique programmable sont terminées, le dispositif commence à exécuter l'application incluse dans le code de démarrage.

Image de la séquence de démarrage du SoC Zynq-7000 de Xilinx

Figure 2 : Dans une séquence de démarrage similaire à des microcontrôleurs conventionnels, un système sur puce Zynq-7000 de Xilinx exécute le code de la mémoire ROM de démarrage qui charge et exécute le chargeur d'amorçage, qui gère à son tour les phases suivantes, notamment l'utilisation d'un train de bits inclus dans le code de démarrage pour configurer la structure logique programmable. (Source de l'image : Xilinx)

Même avec un traitement simplifié du chargement de la logique programmable, les développeurs devaient auparavant faire face au processus de développement FPGA complexe nécessaire pour générer les trains de bits requis. Pour les développeurs espérant tirer parti des performances des FPGA, le processus de développement FGPA conventionnel reste un obstacle majeur à l'implémentation. Grâce à son environnement PYNQ, Xilinx permet de contourner cet obstacle.

Environnement PYNQ

Dans PYNQ, les trains de bits de la logique programmable sont encapsulés dans des bibliothèques pré-construites appelées couches, dont le rôle est similaire à celui des bibliothèques logicielles dans le processus de développement et l'environnement d'exécution. Pendant le processus de chargement au démarrage, les trains de bits associés aux couches requises configurent la structure logique programmable. Cependant, ce processus reste transparent pour les développeurs qui tirent parti de la fonctionnalité de la couche par l'intermédiaire de l'interface de programmation (API) Python associée à chaque couche. Pendant le développement, les ingénieurs peuvent combiner des bibliothèques logicielles et des couches au besoin, en travaillant dans leurs API respectives pour implémenter l'application. Pendant l'exécution, le système de processeur exécute le code de la bibliothèque logicielle comme à l'accoutumée, tandis que la structure logique programmable implémente la fonctionnalité fournie dans la couche. Il en résulte une accélération des performances qui continue à susciter l'intérêt pour les conceptions FPGA pour des applications de plus en plus exigeantes.

Comme son nom l'indique, PYNQ tire parti des gains de productivité de développement associés au langage de programmation Python. Python est devenu l'un des principaux langages de programmation, non seulement du fait qu'il est relativement simple, mais également parce qu'il dispose d'un vaste écosystème qui ne cesse de croître. Les développeurs ont plus de chances de trouver les bibliothèques logicielles nécessaires pour les services d'assistance ou les algorithmes spécialisés dans les référentiels de modules Python open-source. En même temps, les développeurs peuvent implémenter des fonctions essentielles en langage C, car PYNQ utilise l'implémentation en langage C courant de l'interpréteur Python. Cette implémentation fournit un accès facile à des milliers de bibliothèques C existantes et simplifie l'utilisation de bibliothèques en langage C fournies par les développeurs. Même si les développeurs expérimentés peuvent étendre PYNQ avec des couches matérielles spécialisées et des bibliothèques logicielles en langage C, l'avantage de PYNQ réside dans sa capacité à fournir un environnement de développement haute productivité à n'importe quel développeur capable de créer un programme Python.

Étant lui-même un projet open-source, PYNQ repose sur un autre projet open-source, le notebook Jupyter. Les notebooks Jupyter fournissent un environnement particulièrement efficace pour explorer de manière interactive les algorithmes et les applications complexes de prototypage sous Python ou tout autre langage de programmation pris en charge (on en compte actuellement plus de 40). Développé au moyen d'un consensus communautaire dans le cadre du projet Jupyter, un notebook Jupyter combine des lignes de code exécutable avec du texte et des graphiques descriptifs. Cette capacité permet aux développeurs individuels de documenter plus efficacement leur progression sans avoir à passer à un autre environnement de développement. Par exemple, un développeur peut utiliser un notebook qui combine quelques lignes de code nécessaires pour afficher les données avec le graphique généré par le code (Figure 3).

Image d'un notebook Jupyter provenant d'un référentiel d'exemple Xilinx

Figure 3 : Un notebook Jupyter provenant d'un référentiel d'exemple Xilinx combine du texte descriptif, du code exécutable et une sortie associée à une application. (Source de l'image : Xilinx)

La capacité à contenir du code, une sortie et du texte descriptif est possible du fait qu'un notebook Jupyter est un document dynamique géré dans un environnement de développement interactif fourni par un serveur de notebook Jupyter (Figure 4). Dans une session Jupyter, le serveur affiche le fichier notebook dans un navigateur Web conventionnel en utilisant le protocole HTTP, et une combinaison de protocoles HTTP et WebSockets pour le contenu statique et dynamique dans le document affiché. Au niveau du système final, le serveur communique avec un noyau d'exécution de code à l'aide du protocole de messagerie open-source ZeroMQ (ØMQ).

Schéma du déroulement d'une session Jupyter

Figure 4 : Dans une session Jupyter, un serveur notebook affiche le contenu d'un fichier notebook dans un navigateur Web tout en interagissant avec un noyau du système final qui exécute le code. (Source de l'image : Projet Jupyter)

En mode édition, l'utilisateur peut modifier le texte et le code. À son tour, le serveur met à jour le fichier notebook correspondant, qui est un fichier texte contenant une série de paires de clés/valeurs JSON. Ces paires sont appelées cellules dans l'environnement Jupyter. Par exemple, l'affichage du navigateur Web du notebook Jupyter présenté précédemment contient plusieurs cellules pour le code et le texte de marquage (Liste 1).

Copier { "cell_type": "markdown", "metadata": {}, "source": [ "## Error plot with Matplotlib\n", "This example shows plots in notebook (rather than in separate window)."
] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAA[truncated]", "text/plain": [ "<matplotlib.figure.Figure at 0x2f85ef50>" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", " \n", "X = np.arange(len(values))\n", "plt.bar(X + 0.0, values, facecolor='blue', \n", " edgecolor='white', width=0.5, label=\"Written_to_DAC\")\n", "plt.bar(X + 0.25, samples, facecolor='red', \n", " edgecolor='white', width=0.5, label=\"Read_from_ADC\")\n", "\n", "plt.title('DAC-ADC Linearity')\n", "plt.xlabel('Sample_number')\n", "plt.ylabel('Volts')\n", "plt.legend(loc='upper left', frameon=False)\n", "\n", "plt.show()" ] }, 

Liste 1 : Un notebook Jupyter est un fichier texte contenant une série de paires de clés/valeurs JSON contenant des sections de code, un marquage et une sortie, comme illustré ici, qui correspondent au rendu de page illustré à la Figure 3. Notez que la chaîne correspondant à l'image .png dans cette figure a été tronquée à des fins de présentation. (Source du code : Xilinx)

À part ses fonctionnalités de documentation, la puissance de l'environnement Jupyter repose dans sa capacité à exécuter des cellules de code de manière interactive. Il suffit aux développeurs de sélectionner la cellule qui les intéresse dans leur navigateur (bordure bleue dans la Figure 3) et de cliquer sur le bouton d'exécution dans le menu Jupyter en haut de la fenêtre de leur navigateur. À son tour, le serveur notebook Jupyter transfère la cellule de code correspondante vers un noyau d'exécution de code, qui est le noyau Python interactif (IPython) de l'environnement PYNQ. Après l'exécution du code, le serveur met à jour de manière asynchrone la page Web affichée et le fichier notebook avec une sortie générée par le noyau.

PYNQ étend cette même approche au développement basé sur FPGA en intégrant une structure Jupyter incluant un noyau IPython et un serveur Web notebook sur les processeurs Arm du système sur puce Zynq. Le module PYNQ Python inclus dans l'environnement fournit aux programmeurs l'API Python nécessaire pour accéder aux services PYNQ des programmes Python.

Environnement de développement FPGA

Conçu spécialement pour prendre en charge PYNQ, le kit de développement PYNQ-Z1 de Digilent permet aux développeurs de commencer à explorer rapidement les applications accélérées par FPGA, simplement en chargeant l'image Linux amorçable pour PYNQ. La carte PYNQ-Z1 combine un système sur puce Zynq XC7Z020 de Xilinx avec 512 Mo de mémoire RAM, 16 Mo de mémoire Flash et un logement microSD pour une mémoire Flash externe supplémentaire. En plus des commutateurs, boutons, LED et multiples ports entrée/sortie, la carte fournit également des connecteurs pour l'extension vers des outils matériels tiers par l'intermédiaire de l'interface Pmod (module périphérique) de Digilent et de shields Arduino et chipKIT de Digilent. La carte permet également d'utiliser le convertisseur analogique-numérique (CAN) du système sur puce de Zynq, appelé XADC, comme six ports d'entrée analogiques asymétriques ou quatre ports d'entrée analogiques différentiels. Digilent fournit également en supplément le kit de productivité PYNQ-Z1 qui inclut une alimentation, un câble micro USB, une carte microSD préchargée avec une image PYNQ et un câble Ethernet pour mettre à jour ou ajouter des modules Python.

Le développeur peut facilement accéder à toutes les fonctionnalités du système sur puce et de la carte par l'intermédiaire d'un notebook Jupyter. Par exemple, l'accès à l'interface Pmod de la carte pour la lecture des valeurs du CAN et l'écriture des valeurs du convertisseur numérique-analogique (CNA) lors d'un essai en boucle ne nécessite que quelques lignes de code (Figure 5). Une fois que l'importation des modules Python requis est effectuée, la logique programmable du système sur puce est initialisée avec une couche « de base » (cellule 2 de la Figure 5). Comme un pack BSP (Board Support Package) conventionnel, cette couche de base fournit un accès aux périphériques de la carte.

Image d'un notebook Jupyter inclus dans le référentiel d'exemple de Xilinx

Figure 5 : Un notebook Jupyter inclus dans le référentiel d'exemple de Xilinx illustre le schéma de conception simple associé à l'accès aux services matériels pour des transactions entrée/sortie. (Source de l'image : Xilinx)

Les développeurs doivent simplement faire appel à des modules importés pour lire et écrire les valeurs (cellule 3 de la Figure). Dans le notebook d'exemple illustré, le serveur notebook émet chaque cellule en séquence et met à jour le notebook avec les résultats générés. Dans ce cas, l'unique valeur de sortie est 0,3418, mais toutes les erreurs d'exécution s'afficheront en tant que piles de traçage Python normales alignées à leurs cellules de code respectives.

Création d'applications complexes

Combinée au large éventail de modules Python disponibles, cette approche faussement simple du développement d'applications embarquées cache une plateforme puissante pour implémenter rapidement des applications complexes impliquant des tâches de calcul intensives. Par exemple, les développeurs peuvent rapidement implémenter une webcam de détection faciale à l'aide de l'entrée HDMI PYNQ-Z1 et de la bibliothèque de vision par ordinateur populaire OpenCV. Une fois que le chargement de la couche de base et de l'interface de webcam est effectué, les développeurs initialisent une fonction videoIn d'objet de caméra OpenCV (Figure 6). La lecture de l'image vidéo est alors aussi simple qu'un appel de la fonction videoIn.read(), qui renvoie ici frame_vga.

Image d'un notebook Jupyter inclus dans le référentiel d'exemple de Xilinx

Figure 6 : Un notebook Jupyter inclus dans le référentiel d'exemple Xilinx montre la façon dont les développeurs peuvent créer rapidement un système de reconnaissance faciale en combinant les ressources matérielles de la carte de développement PYNQ-Z1 avec les fonctions de traitement d'image puissantes disponibles dans la bibliothèque OpenCV (cv2). (Source de l'image : Xilinx)

Lors d'une étape suivante, gérée en tant que cellule distincte dans le notebook, les développeurs créent des objets de classificateur OpenCV (cv2) à l'aide de critères prédéfinis et ajoutent des zones de délimitation pour identifier les fonctionnalités (dans cet exemple, zones vertes pour les yeux et bleues pour les visages). Dans une autre paire de cellules, l'application se termine après avoir affiché les résultats à l'aide de la sortie HDMI de la carte (Figure 7).

Image des dernières cellules dans le notebook de détection faciale de webcam Xilinx

Figure 7 : Les dernières cellules dans le notebook de détection faciale de webcam Xilinx montrent l'utilisation de classificateurs OpenCV, dont les résultats sont utilisés pour ajouter des zones de délimitation pour les images d'origine, qui s'affichent à l'aide du port de sortie HDMI de la carte de développement PYNQ-Z1. (Source de l'image : Xilinx)

Grâce à leur capacité à créer, tester et partager des discussions concernant un logiciel complexe de manière interactive, les notebooks Jupyter figurent parmi les solutions préférées des scientifiques et ingénieurs qui cherchent à optimiser des algorithmes pour des applications d'intelligence artificielle. À mesure que les tâches évoluent, le notebook affiche non seulement du code et ses résultats, mais également l'analyse du développeur concernant les résultats, ce qui fournit une sorte de récit de calcul qui peut être partagé entre les membres de l'équipe et les collègues.

Néanmoins, les développeurs doivent comprendre qu'il est peu probable que les notebooks constituent des référentiels pour les efforts axés sur la production. Par exemple, le fait qu'ils incluent d'importantes chaînes codées hexadécimales pour des données d'image (voir la section tronquée de la Liste 1) augmente non seulement la taille du document, mais peut également compliquer les différentes méthodes utilisées par les systèmes de commande de version source typiques. L'entrelacement de code et de texte non fonctionnel peut compliquer davantage la migration du code créé dans les premières phases analytiques pour les processus de développement au niveau de la production. Toutefois, pour une exploration du code et un prototypage rapide, les notebooks Jupyter offrent un environnement de développement puissant.

Conclusion

Les FPGA fournissent la dynamisation de performances nécessaire pour répondre aux demandes croissantes en matière de systèmes embarqués conçus pour l'IoT, la vision par ordinateur, l'automatisation industrielle, le secteur automobile et bien plus encore. Bien que les méthodes de développement de FPGA traditionnelles restent des obstacles pour de nombreux développeurs, l'émergence de l'environnement de développement PYNQ Python basé sur des notebooks Jupyter offre une alternative efficace. Grâce à une carte de développement spécialement conçue pour prendre en charge PYNQ, même des développeurs disposant de peu d'expérience en FPGA peuvent implémenter rapidement des conceptions capables de tirer pleinement parti des performances des FPGA pour accélérer les applications impliquant des tâches de calcul intensives.

DigiKey logo

Avertissement : les opinions, convictions et points de vue exprimés par les divers auteurs et/ou participants au forum sur ce site Web ne reflètent pas nécessairement ceux de DigiKey ni les politiques officielles de la société.

À propos de l'auteur

Image of Stephen Evanczuk

Stephen Evanczuk

Stephen Evanczuk affiche plus de 20 ans d'expérience dans la rédaction de contenu pour et sur l'industrie électronique, couvrant un large éventail de sujets, notamment le matériel, les logiciels, les systèmes et les applications, y compris l'IoT. Il a obtenu son doctorat (Ph.D.) en neurosciences sur les réseaux neuronaux et a travaillé dans l'industrie aérospatiale sur les systèmes sécurisés massivement distribués et les méthodes d'accélération par algorithmes. Actuellement, lorsqu'il n'écrit pas d'articles techniques, il travaille sur l'application de l'apprentissage approfondi pour les systèmes de reconnaissance et de recommandation.

À propos de l'éditeur

Rédacteurs nord-américains de DigiKey