IMA5 2018/2019 P19 : Différence entre versions
(→Projet petalinux standalone) |
(→Projet petalinux standalone) |
||
Ligne 388 : | Ligne 388 : | ||
A ce stade, nous avons une image bootable, mais aucun OS. Nous allons donc compiler une image petalinux. | A ce stade, nous avons une image bootable, mais aucun OS. Nous allons donc compiler une image petalinux. | ||
− | :N.B. Petalinux étant utilisé sous Linux, l'intégralité du projet Vivado et XSDK doit être réalisée sur Linux. | + | :N.B. Petalinux étant utilisé sous Linux, l'intégralité du projet Vivado et XSDK doit être réalisée sur Linux, il y aura sinon des conflits lors de la compilation du kernel à cause du wrapper hardware qui n'est pas le même selon les OS. |
+ | |||
+ | Pour commencer, il faut créer un projet petalinux: | ||
+ | petalinux-create -t project --name petalinux-zybo --template zynq | ||
+ | cd petalinux-zybo/ | ||
+ | |||
+ | Ensuite, décrire la partie hardware: | ||
+ | petalinux-config --get-hw-description VIVADO_PROJECT/VIADO_PROJECT.sdk/ | ||
+ | |||
+ | :cela peut prendre du temps (~15 mins sur une machine dual core) | ||
+ | |||
+ | Puis configurer le kernel et le système de fichier: | ||
+ | petalinux-config -c rootfs | ||
+ | petalinux-config -c kernel | ||
+ | |||
+ | :cela va générer des fichiers permettant la compilation (~30 mins sur une machine dual core) | ||
+ | |||
+ | Dans mon cas, j'ai laisser la configuration par défaut du kernel et copier le fichier de configuration obtenu grâce au bsp (vu dans la partie Projet petalinux à partir d'un fichier bsp (board support package)) afin d'avoir un système de fichier propre: | ||
+ | cp -r ../PETALINUX_FROM_BSP_FOLDER/project-spec/configs ./project-spec | ||
+ | |||
+ | Enfin, petalinux peut être build: | ||
+ | petalinux-build | ||
+ | :(~30 mins sur une machine dual core) | ||
+ | |||
+ | On peut maintenant créer le fichier BOOT.BIN: | ||
+ | petalinux-package --boot --format BIN --fsbl VIVADO_PROJECT/VIVADO_PROJECT.sdk/FSBL/Debug/FSBL.elf --fpga VIVADO_PROJECT/VIVADO_PROJECT.sdk/design_1_wrapper_hw_platform_0/design_1_wrapper.bit --u-boot --boot-device sd | ||
+ | |||
+ | On dispose alors de ./BOOT.BIN ainsi que ./images/linux/image.ub (créé lors de petalinux-build et décrivant le système de fichier) que l'on peut placer dans la première partition de la carde SD pour booter petalinux sur la Zybo. | ||
+ | |||
+ | Cependant, nous n'avons pas notre application C dans le système de fichier, il faut donc le faire. | ||
+ | |||
+ | D'abord il faut créer l'application avec petalinux: | ||
+ | petalinux-create -t apps --name sampleapp --template install --enable | ||
+ | :il existe aussi des templates pour c et c++ permettant de mettre les fichiers sources couplés à un Makefile pour recompiler l'application à chaque build | ||
+ | |||
+ | Ensuite on place l’exécutable dans le dossier adéquat: | ||
+ | cp VIVADO_PROJECT/VIVADO_PROJECT.sdk/APP_NAME/Debug/APP_NAME.elf ./project-spec/meta-user/recipes-apps/sampleapp/files/sampleapp | ||
+ | :il faut renommer le fichier .elf pour qu'il ai le même nom que l'application créer avec petalinux-create -t apps | ||
+ | |||
+ | On relance la commande petalinux-build (cette fois ci cela devrait durer environ 5 mins) puis petalinux-package, on place BOOT.BIN et image.ub sur la première carte sd. Une fois la Zybo mise sous tension, on peut s'y connecter via minicom (ou putty, tera term, ...) pour accéder au shell du petalinux. root/root pour s'y connecter en root, et on remarque bien la présence de notre executable dans /usr/bin. | ||
+ | Cependant, si on tente de l'exécuter: | ||
+ | /usr/bin/sampleapp | ||
+ | |||
+ | On observe que la console retourne: | ||
+ | Illegal instruction | ||
+ | |||
+ | En fait, lors de la création de l'application sur XSDK, le projet et par défaut en OS "standalone", les drivers ajoutés par XSDK ne fonctionnent donc que sur application tournant sur du "baremetal" (sans Linux derrière). | ||
+ | |||
+ | Il faut donc créer un projet avec en choisissant un OS Linux, importer les librairies qui vont biens (voire en écrire dans certains cas), et compiler des drivers pour que Linux puisse utiliser l'hardware de la Zybo. | ||
==L'interface de connexion AXI== | ==L'interface de connexion AXI== |
Version du 21 janvier 2019 à 12:27
Sommaire
Détection de menaces IOT sur FPGA
Présentation générale
De nos jours, l'informatique s'oriente vers une philosophie microservices. C'est-à-dire que l'on conçoit une multitude de tâches simples, qui permettent de réaliser un système plus complexe une fois rassemblées. L'IOT (internet des objets) est conçu sur ce principe. Par exemple, dans le cadre de la domotique, une maison est "connectée" grâce à une base sur laquelle viennent s'appairer plusieurs objets (capteurs, actionneurs, indicateurs, ...), constituant alors un réseau complet. Le problème est que par soucis de praticité, la grande majorité des objets IOT fonctionnent sur batterie, ce sont donc de très petit système incapables de se défendre en cas d'attaques.
Description
Ce projet a pour but de proposer un système permettant de détecter de possibles menaces sur un réseau IOT en temps réel.
Objectifs
- Lister les attaques possibles sur un réseau IOT
- Mettre en place un réseau IOT permettant d'étudier certaines attaques
- Récupérer les différentes informations des objets connectés sur un FPGA
- Agréger ces données pour en sortir des modèles
- A partir de ces modèles, être capable de détecter de possibles attaques
- Proposer un système de dashboard / alerting permettant de visualiser l'état du réseau
Cahier des charges
Le système sera composé d'une base (sur laquelle les objets seront connectés) constituée d'un FPGA couplé à un processeur fonctionnant avec linux. Ce système pourra détecter des menaces réseaux (déni de service, homme du milieu, ...) ou des menaces physiques (brouillage, diminution de la batterie, ...).
- Il faudra dans un premier temps recenser les différentes attaques possibles sur un réseau IOT puis déterminer celles qui pourront être détectées (par exemple le eavesdropping - consistant à juste écouter le traffic - ne sera pas détectable).
- Ensuite, un réseau IOT simple sera mis en place, ce dernier sera constitué de la base (décrite ci-dessus) et de quelques nœuds communicant avec cette base. Deux cas seront à prendre en compte, le premier est une communication direct entre un nœud et la base, le second est une communication d'un nœud vers un autre noeud qui relayera vers la base. Le protocole de communication sera choisi parmi ceux qui sont les plus utilisés en IOT (BLE, Z-Wave et ZigBee pour les courtes distances ; Lora et SigFox pour les distances plus longues).
- Une fois le réseau mis en place, il faudra récolter certaines informations concernant les nœuds et les agréger sur la base dans le but de définir les comportements normaux des nœuds.
- Après avoir définit ces comportements normaux, les attaques retenues dans le point 2 seront testées sur le réseau afin de voir leur(s) impacte(s) sur les données récoltées et d'en déduire des seuils permettant de basculer entre l'état normal et l'état anormal.
- Enfin, un système de dashboard sera proposé pour visualiser l'état du réseau IOT et émettre des alertes.
Réalisation du Projet
Liste non exhaustive des attaques possibles sur un réseau IOT
Couche Applicative
La couche applicative permet d'améliorer l'expérience utilisateur, elle induit aussi de possibles attaques :
- Injection de code par l'exploitation de bug : le plus souvent réaliser avec un "buffer overflow" qui va corrompre la mémoire vive et permettre d'injecter du code exécutable.
- Autorisation : beaucoup d'objets IOT ont la configuration par défaut (par exemple admin:admin comme logins) ce qui permet d'avoir un accès direct au shell de l'objet en question.
Couche Réseau
La couche réseau constitue les différents protocoles et trames utilisés par les objets pour communiquer, c'est la couche ou l'on retrouve le plus d'attaques possibles :
- Déni de service (DOS) : envoyer un nombre important de requêtes pour submerger le trafic.
- Homme du milieu (MITM) : intercepter le trafic venant d'un objet puis le transmettre à la base (ou inversement), permettant d'écouter ou de modifier le trafic de manière incognito.
- Sybil attack (multiplication d'identités) : créer de fausses identités sur le réseau afin de le corrompre.
- Sinkhole / Blackhole : intercepter tout le trafic et le router ailleurs (sinkhole) ou ne pas le router (blackhole), nécessite de se faire passer pour la base ou un relayeur.
- Sniffing : écouter le trafic pour l'analyser, permet ensuite de faire du DOS ou MITM.
- Hello spamming : se faire passer pour un nouvel objet et envoyer un nombre important de requête d'appairage, permet de faire du DOS ou d'obtenir la connexion au réseau.
Couche de Perception
La couche de perception permet aux objets d’émettre et de recevoir le trafic, rendant les objets vulnérables à plusieurs attaques :
- Eavesdropping : écouter le trafic.
- Brouillage RF : submerger les bandes de fréquences pour noyer les communications.
- Spoofing : se faire passer pour un nœud valide.
- Bloquer la mise en veille : envoyer des signaux de réveil de manière répétée afin de sur-consommer la batterie des objets.
Couche Physique
Enfin la couche physique, constituée du matériel à proprement parlé, ajoute une attaque à la liste :
- Ajout d'un objet malicieux
Préparation du processeur
Le processeur utilisé pour le projet est un Zynq xc7z010 (monté sur une carte Zybo), qui est constitué de deux cœurs de type CPU et d'une partie logique de type FPGA, comme indiqué sur le schéma suivant : https://www.xilinx.com/content/dam/xilinx/imgs/block-diagrams/zynq-mp-core-dual.png
On remarque les interfaces AXI qui permettrons d'établir la communication entre le CPU et le FPGA.
Pour commencer, il faut installer une distribution linux de type petalinux sur la partie CPU du processeur. Diligent propose une distribution qui est adaptée à cette famille board.
- Prérequis
- Vivado WebPack version 2017.2 ou 2017.4, de préférence sur une machine linux (j'ai donc installé une machine virtuelle de type ubuntu sur mon pc).
- Le SDK Xilinx (proposé lors de l'installation de Vivado).
- Le fichier de configuration des cartes installé dans /opt/Xilinx/Vivado/VERSION/data/boards.
- Note
- Les version ne sont pas toujours compatibles entre elles. Par exemple, configurer un projet petalinux de version 2015.4 avec le CLI 2017.4 ne fonctionnera pas puisque le système de fichier utilisé est différent. Dans mon cas j'utilise la version 2017.4 sur tous les outils.
- Les builds avec Vivado et Petalinux sont longs, une machine avec 4 cœurs ou plus est préférable.
- Sources
Installation de l'outil petalinux
Petalinux est un outil en ligne commande permettant de compiler des images linux, l'exécutable pour l'installation peut être trouvé ici, il choisir la même version que l'installation de Vivado.
L'installation requiert plusieurs librairies et échouera si l'une d'entre elles n'est pas installée (à exécuter en tant que root):
apt-get install g++ chrpath xvfb xterm tofrodos iproute gawk gcc git-core make net-tools libncurses5-dev tftpd zlib1g-dev libssl-dev flex bison libselinux1 lib32z1 lib32ncurses5 lib32stdc++6 libbz2-1.0:i386 libtool-bin
- L'installation peut être lancée même si il manque l'une de ces librairies, mais elle échouera au bout d'une dizaine de minutes. D'autre part, en cas d'échec de l'installation, le dossier de destination n'est pas vidé, entraînant alors aussi un échec lors d'une nouvelle tentative.
L'installation peut ensuite être lancée (/opt/Petalinux doit exister et disposer d'au moins 20Go):
PATH-TO/petalinux-v2017.4-final-installer-dec.run opt/PetaLinux
Une fois l'installation terminée (ce qui peut prendre environ 15 mins), vérifiez que vous êtes bien configurer en bash et sourcez les scripts nécessaires :
sudo dpkg-reconfigure dash source /opt/Petalinux/settings.sh source /opt/Xilinx/Vivado/2017.4/settings64.sh
(les sources peuvent être ajoutées directement dans /etc/bash.bashrc)
Préparation de la carte SD
L'image linux va être booté depuis une carte SD. Il faut donc placer le jumper JP5 sur la carte Zybo en position SD/QSP1.
La carde SD doit être formatée avec une partition fat32 d'1Go pour le boot et d'une partition ext4 pour le système de fichier.
Pour ce faire il faut supprimer le formatage par défaut de la carte sd. Je l'ai fait depuis le powershell de Windows :
> DISKPART DISKPART > list disk DISKPART > select disk 1 DISKPART > disk clean DISKPART > exit
Ensuite j'ai monté la carte sd sur ma machine virtuelle pour la formater. Pour une machine virtuelle tournant sur VirtualBOX :
- Obtenez le DeviceID du disque (depuis un powershell):
wmic diskdrive list brief
- Creez le fichier vmdk (depuis un powershell en tant qu'administrateur):
C:\Program Files\Oracle\VirtualBox\VBoxManage internalcommands createrawvmdk -filename "%USERPROFILE%/Documents/sdcard.vmdk" -rawdisk "\\.\PHYSICALDRIVE1"
- Ouvrez VirtualBOX en tant qu'administrateur et ajoutez le disque à la machine virtuelle (la vm doit être éteinte):
selectionner la vm > Configuration > Stockage > Contrôleur SATA > Ajoute un disque dur > Choisir un disque existant > ~/Documents/sdcard.vmdk
Pour formater la carte sd avec linux, cherchez la partition (à exécuter en tant que root):
fdisk -l
- Puis lancez l'outil fdisk (en tant que root) pour configurer la carte sd (/dev/sdb dans mon cas):
> fdisk /dev/sdb fdisk > n créer la première partition fdisk > p en fait une primary fdisk > 1 lui affecte le numéro 1 fdisk > premier block (laissez par defaut) fdisk > +1G dernier block pour former une partition d'1Go fdisk > a en fait la partition de boot fdisk > n créer la deuxième partition fdisk > p en fait une primary fdisk > 2 lui affecte le numéro 2 fdisk > premier block (laissez par defaut, après la fin de la première partition) fdisk > dernier block (laissez par defaut pour utiliser l'espace restant) fdisk > w applique les changement fdisk > p affiche les partitions pour vérifier fdisk > q quitter
- Configurez les partitions en fat32 et ext4 (à exécuter en tant que root):
mkfs.vfat -F 32 -n boot /dev/sdb1 mkfs.ext4 -L root /dev/sdb2
Projet petalinux à partir d'un fichier bsp (board support package)
Diligent propose propose une image petalinux simple à utiliser. Elle peut être téléchargée ici.
- Créez le projet à partir du fichier téléchargé (cela peut prendre un certain temps):
petalinux-create -t project -s Petalinux-Zybo-2017.4-1.bsp
Il y a une images précompilée dans le dossier Petalinux-Zybo-2017.4-1/pre-built/linux/images/, copiez BOOT.BIN et image.ub dans la première partition de la carte sd. Placez la carte sd dans la Zybo et mettez la carte sous tension.
Pour se connecter à la carte, utilisez minicom sur le port série (à exécutez en root):
minicom -D /dev/ttyUSB1
Le port série doit être configuré de la façon suivante:
- baud rate = 115200
- data bits = 8
- stop bits = 1
- flow control = none
- parity = none
Le shell du petalinux devrait apparaître. Pour revoir le boot, cliquez sur le bouton BTN7 (PS-SRST) de la carte.
Il est possible de programmer la partie FPGA depuis petalinux en écrivant le bitstream dans /dev/xdevcfg :
cat bitstream.bit > /dev/xdevcfg
Néanmoins, dans mon cas, ceci écrase la configuration de petalinux et le rend inutilisable.
Cette image petalinux est adaptée à une définition hardware (block design) prédéfinie qui est la suivante :
Si on tente de compiler le kernel linux avec un block design différent, le device tree ne correspondra pas et la compilation échouera.
Il faut donc créer sont propre projet petalinux pour y intégrer un block design custom.
Projet petalinux standalone
Il est possible de créer une image Petalinux à partir d'un projet Vivado, incluant alors la partie Hardware (block design), la partie FPGA (bitstream), ainsi que la partie linux (FSBL). Pour commencer, je me suis basé sur un tutoriel de Digilent pour créer un IP utilisant l'interface AXI.
Projet Vivado
On commence par créer un projet dans vivado de type RTL, sans ajouter de module verilog initial, avec un fichier de contrainte (contraintes.xdc) et en choisissant la carte zybo (le fichier de configuration de cartes doit être installé).
Avant de créer de définir la partie hardware, il faut créer l'IP que nous allons utiliser (l'IP sera une PWM simple pilotant les leds). Pour ce faire, il faut se rendre dans "Tools > Create and Package New IP". Dans la nouvelle fenêtre, il faut choisir "Create a new AXI4 peripheral" (AXI étant une interface permettant de faire communiquer le CPU et le FPGA). Cela va créer un repository local contenant cet IP (et potentiellement les prochains IPs), ce repository peut être utilisé dans d'autres projets.
On peut ensuite configurer l'IP, dans ce cas on utilisera l'interface AXI lite en mode slave (puisqu'il sera piloté par le CPU) avec un bus de 32 bits et 4 registres (on notera qu'il y a une option pour inclure les interruptions) :
Maintenant l'IP créer, on remarque deux fichier verilog ({IP_NAME}_v1_0.v et {IP_NAME}_v1_0_S00_AXI_inst.v), il faut leur ajouter la partie logique. Puisque la PWM pilote les leds, les ports à ajouter sont les 4 leds (à modifier dans les 2 fichiers) :
// Users to add ports here output wire PWM0, output wire PWM1, output wire PWM2, output wire PWM3, // User ports ends
On ajoute aussi le paramètre d'amplitude de la PWM dans les 2 fichiers (ce paramètre pourra être modifier dans le block design du projet) :
// Users to add parameters here parameter integer PWM_COUNTER_MAX = 1024, // User parameters ends
La partie logique (dans le fichier _inst.v) :
// Add user logic here //registre définissant l'amplitude de la PWM reg [15:0] counter = 0; //incrémente le registre à chaque front montant de la clock de l'IP always @(posedge S_AXI_ACLK) begin if(counter < PWM_COUNTER_MAX-1) begin counter <= counter + 1; else //réinitialise le registre si il est égale au paramètre PWM_COUNTER_MAX counter <= 0; end end //signal PWM //slv_reg correspont au registres d'AXI lites créer précédemment //si la valeur du registre slave est inférieure au compteur, l'ouput est inactif (1'b0), sinon il est actif //la valeur du registre slave sera modifié par le code c grâce au memory mapping assign PWM0 = slv_reg0 < counter ? 1'b0 : 1'b1; assign PWM1 = slv_reg1 < counter ? 1'b0 : 1'b1; assign PWM2 = slv_reg2 < counter ? 1'b0 : 1'b1; assign PWM3 = slv_reg3 < counter ? 1'b0 : 1'b1; // User logic ends
Enfin on modifie la définition du bus pour y inclure nos variables (dans le fichier {IP_NAME}_v1_0) :
// Instantiation of Axi Bus Interface S00_AXI My_PWM_Core_v1_0_S00_AXI # ( .C_S_AXI_DATA_WIDTH(C_S00_AXI_DATA_WIDTH), .C_S_AXI_ADDR_WIDTH(C_S00_AXI_ADDR_WIDTH), .PWM_COUNTER_MAX(PWM_COUNTER_MAX) ) My_PWM_Core_v1_0_S00_AXI_inst ( .PWM0(PWM0), .PWM1(PWM1), .PWM2(PWM2), .PWM3(PWM3), .S_AXI_ACLK(s00_axi_aclk), .S_AXI_ARESETN(s00_axi_aresetn), .S_AXI_AWADDR(s00_axi_awaddr), .S_AXI_AWPROT(s00_axi_awprot), .S_AXI_AWVALID(s00_axi_awvalid), .S_AXI_AWREADY(s00_axi_awready), .S_AXI_WDATA(s00_axi_wdata), .S_AXI_WSTRB(s00_axi_wstrb), .S_AXI_WVALID(s00_axi_wvalid), .S_AXI_WREADY(s00_axi_wready), .S_AXI_BRESP(s00_axi_bresp), .S_AXI_BVALID(s00_axi_bvalid), .S_AXI_BREADY(s00_axi_bready), .S_AXI_ARADDR(s00_axi_araddr), .S_AXI_ARPROT(s00_axi_arprot), .S_AXI_ARVALID(s00_axi_arvalid), .S_AXI_ARREADY(s00_axi_arready), .S_AXI_RDATA(s00_axi_rdata), .S_AXI_RRESP(s00_axi_rresp), .S_AXI_RVALID(s00_axi_rvalid), .S_AXI_RREADY(s00_axi_rready) );
L'IP est presque prêt à être utilisé, il faut configurer le paramètre PWM_COUNTER_MAX pour qu'il puisse être modifier dans le block design du projet. Pour cela, il faut se rendre dans l'onglet "Package IP", puis dans "Customization Parameters" et cliquer sur "Merge Changes from Customization Parameters Wizard". Se rendre dans "Customization GUI" double cliquer sur le paramètre et cocher "Visible in Customization GUI", puis le faire glisser avec les autres paramètres dans "Page 0". Bien sûr, si le paramètre n'a pas besoin d'être modifié depuis le block desgin, cette étape peut être passée.
Enfin, se rendre dans l'onglet "Review and Package" et cliquer sur "Re-Package IP". La fenêtre va alors se fermer et on peut créer notre block design (qui définie la partie hardware).
On crée un nouveau block design et on y ajoute l'IP ZYNQ7 Processing System ainsi que l'IP que l'on a crée ci-dessus. Les connections étant simples on peut utiliser "Run Block Automation" et "Run Connection Automation", puis ajouter les outputs PWM0 à PWM3 en faisant un clique droit sur la variable du même nom (sur l'IP) et "Create PORT". Le block design devrait être comme suit :
On peut maintenant créer le module verilog associé au block design, en faisant un clique droit sur le fichier du block design (dans l'onglet "Sources") et en choissisant "Create HDL Wrapper".
Pour finir, il faut éditer le fichier de contraintes (contraintes.xdc) pour définir les pins des leds à utiliser :
##IO_L23P_T3_35 set_property PACKAGE_PIN M14 [get_ports PWM0] set_property IOSTANDARD LVCMOS33 [get_ports PWM0] ##IO_L23N_T3_35 set_property PACKAGE_PIN M15 [get_ports PWM1] set_property IOSTANDARD LVCMOS33 [get_ports PWM1] ##IO_0_35 set_property PACKAGE_PIN G14 [get_ports PWM2] set_property IOSTANDARD LVCMOS33 [get_ports PWM2] ##IO_L3N_T0_DQS_AD1N_35 set_property PACKAGE_PIN D18 [get_ports PWM3] set_property IOSTANDARD LVCMOS33 [get_ports PWM3]
Le fichier de contraintes de base de la Zybo peut être trouvé à cette adresse
La partie sur Vivado est maintenant terminée, et on peut passer à la partie XSDK. Pour ce faire cliquez sur File > Export > Export Hardware > cochez "Include Bitstream" > OK. Puis File > Launch SDK > OK.
Xilinx SDK
Tout d'abord, on remarque bien la présence de notre wrapper dans les sources :
Cette structure a été créée lorsque nous avons exporté la partie hardware.
Il faut maintenant concevoir le code C qui va piloter l'IP. Créez une nouvelle application en allant dans File > New > Application Project. Donnez lui un nom et vérifiez que "Hardware Platform" est bien le dossier vu sur l'image ci-dessus. Cliquez sur next puis choisissez le template "Hello World", cela permettra d'inclure les librairies nécessaires. Enfin supprimez helloworld.c dans l'explorateur de fichier et créez un fichier main.c avec le contenu suivant :
#include "xparameters.h" #include "xil_io.h" #define MY_PWM 0x43C00000 //This value is found in the Address editor tab in Vivado (next to Diagram tab) int main(){ int num=0; int i; while(1){ if(num == 256) num = 0; else num++;
//écrit dans le registre slv_reg0 Xil_Out32(MY_PWM, num); //écrit dans le registre slv_reg1 Xil_Out32((MY_PWM+4), num); //écrit dans le registre slv_reg2 Xil_Out32((MY_PWM+8), num); //écrit dans le registre slv_reg3 Xil_Out32((MY_PWM+12), num); for(i=0;i<300000; i++); } }
Sauvegardez (le code est build automatiquement).
On peut maintenant tester le fonctionnement. D'abord, il faut placer le jumper JP5 de la Zybo sur JTAG (si ce n'est pas déjà le cas).
On programme la partie FPGA : Xilinx > Program FPGA, verifiez que le bitstream soit le bon et cliquez sur Program. Les leds devraient s'allumer et le rester (c'est normal puisque seul la partie FPGA est programmée, donc les registres slaves de notre IP ont toujours la même valeur). Ensuite, lancez l'application C, clique droit sur le projet (pas le bsp) > Run As > Launch on Hardware (System Debugger). La PWM entre en action et les leds devraient "pulser".
Création d'une image bootable
Pour le moment l'application est chargée directement en RAM, donc lorsque la Zybo est mise hors tension toutes les configurations précédentes sont perdues. Pour que l'application soit présente à la mise sous tension de la carte, il faut créer une image bootable.
Il faut commencer par créer le FSBL (first stage boot loader) qui va donner les instructions au CPU à la mise sous tension. Dans XSDK, cliquez sur File > New > Application Project, donnez un nom au projet (par ex: FSBL), vérifiez que "Hardware Platform" est le bon dossier, cliquez sur Next puis choisissez le template "Zynq FSBL". Le projet devrait être build automatiquement, on peut maintenant créer l'image bootable.
Toujours dans XSDK, cliquez sur Xilinx > Create Boot Image. Dans la nouvelle fenêtre, choisissez le dossier de destination. Dans la partie "Boot image partitions", cliquez sur Add et cherchez FSBL.elf dans la section File path (le fichier se trouve dans VIVADO_PROJECT/VIVADO_PROJECT.sdk/FSBL/Debug), choisissez "bootloader" dans Partition type. Répétez l'opération pour le bitstream et le .elf de l'application C avec le type "datafile" :
- N.B. L'ordre des fichiers dans Boot image partitions doit être respecter.
Enfin, placez le fichier BOOT.BIN qui vient d'être généré dans la première partition de la carte SD. Placez le jumper JP5 de la Zybo sur SD (si ce n'est pas déjà le cas) et mettez la carte sous tension. Les leds devraient avoir le même comportement vu précédemment, mais cette fois ci l'application est exécuté à la mise sous tension de la carte.
Création de l'image Petalinux
A ce stade, nous avons une image bootable, mais aucun OS. Nous allons donc compiler une image petalinux.
- N.B. Petalinux étant utilisé sous Linux, l'intégralité du projet Vivado et XSDK doit être réalisée sur Linux, il y aura sinon des conflits lors de la compilation du kernel à cause du wrapper hardware qui n'est pas le même selon les OS.
Pour commencer, il faut créer un projet petalinux:
petalinux-create -t project --name petalinux-zybo --template zynq cd petalinux-zybo/
Ensuite, décrire la partie hardware:
petalinux-config --get-hw-description VIVADO_PROJECT/VIADO_PROJECT.sdk/
- cela peut prendre du temps (~15 mins sur une machine dual core)
Puis configurer le kernel et le système de fichier:
petalinux-config -c rootfs petalinux-config -c kernel
- cela va générer des fichiers permettant la compilation (~30 mins sur une machine dual core)
Dans mon cas, j'ai laisser la configuration par défaut du kernel et copier le fichier de configuration obtenu grâce au bsp (vu dans la partie Projet petalinux à partir d'un fichier bsp (board support package)) afin d'avoir un système de fichier propre:
cp -r ../PETALINUX_FROM_BSP_FOLDER/project-spec/configs ./project-spec
Enfin, petalinux peut être build:
petalinux-build
- (~30 mins sur une machine dual core)
On peut maintenant créer le fichier BOOT.BIN:
petalinux-package --boot --format BIN --fsbl VIVADO_PROJECT/VIVADO_PROJECT.sdk/FSBL/Debug/FSBL.elf --fpga VIVADO_PROJECT/VIVADO_PROJECT.sdk/design_1_wrapper_hw_platform_0/design_1_wrapper.bit --u-boot --boot-device sd
On dispose alors de ./BOOT.BIN ainsi que ./images/linux/image.ub (créé lors de petalinux-build et décrivant le système de fichier) que l'on peut placer dans la première partition de la carde SD pour booter petalinux sur la Zybo.
Cependant, nous n'avons pas notre application C dans le système de fichier, il faut donc le faire.
D'abord il faut créer l'application avec petalinux:
petalinux-create -t apps --name sampleapp --template install --enable
- il existe aussi des templates pour c et c++ permettant de mettre les fichiers sources couplés à un Makefile pour recompiler l'application à chaque build
Ensuite on place l’exécutable dans le dossier adéquat:
cp VIVADO_PROJECT/VIVADO_PROJECT.sdk/APP_NAME/Debug/APP_NAME.elf ./project-spec/meta-user/recipes-apps/sampleapp/files/sampleapp
- il faut renommer le fichier .elf pour qu'il ai le même nom que l'application créer avec petalinux-create -t apps
On relance la commande petalinux-build (cette fois ci cela devrait durer environ 5 mins) puis petalinux-package, on place BOOT.BIN et image.ub sur la première carte sd. Une fois la Zybo mise sous tension, on peut s'y connecter via minicom (ou putty, tera term, ...) pour accéder au shell du petalinux. root/root pour s'y connecter en root, et on remarque bien la présence de notre executable dans /usr/bin. Cependant, si on tente de l'exécuter:
/usr/bin/sampleapp
On observe que la console retourne:
Illegal instruction
En fait, lors de la création de l'application sur XSDK, le projet et par défaut en OS "standalone", les drivers ajoutés par XSDK ne fonctionnent donc que sur application tournant sur du "baremetal" (sans Linux derrière).
Il faut donc créer un projet avec en choisissant un OS Linux, importer les librairies qui vont biens (voire en écrire dans certains cas), et compiler des drivers pour que Linux puisse utiliser l'hardware de la Zybo.