Laboratoire 1 : Construction d’une chaîne de compilation pour Raspberry Pi Zero W

Systèmes embarqués temps réel (GIF-3004)

Objectifs

Ce travail pratique vise les objectifs suivants :

  1. Mettre en place un environnement de développement complet;
  2. Comprendre les mécanismes derrière la compilation croisée;
  3. Maîtriser les rudiments du débogage et du profilage à distance;
  4. Analyser et déboguer un code C simple;
  5. Se familiariser avec l’utilisation du Raspberry Pi Zero W.

Préparation et outils nécessaires

La carte MicroSD du kit qui vous a été fourni contient déjà l’image système nécessaire au cours. Toutefois, dans le cas où vous voudriez revenir à l’état initial de l’image, ou simplement créer une copie, vous pouvez télécharger le fichier .img contenant l’image du cours.

La première des tâches à réaliser est de démarrer le Raspberry Pi Zero W, de mettre en place sa configuration initiale et de vous assurer de son bon fonctionnement. Par la suite, vous devrez installer sur votre ordinateur l’environnement de développement et de compilation croisée qui vous servira tout au long de la session.

Important : le Raspberry Pi étant un ordinateur à part entière, il est techniquement possible de n’utiliser que ce dernier et y travailler localement en se passant de l’environnement de développement à distance. Cela n’est toutefois pas représentatif du développement des systèmes embarqués en pratique, où il est souvent impossible de travailler directement sur le matériel cible, que ce soit par manque de puissance ou par d’autres problèmes pratiques (pensons par exemple à un Raspberry Pi embarqué dans un dispositif lourd et encombrant). De plus, pour beaucoup de travaux, la puissance limitée du Raspberry Pi Zero W et son nombre de ports limité rendraient malaisée une telle utilisation. Pour cette raison, dans le cadre du cours, il vous est interdit d’utiliser le Raspberry Pi de cette manière, sauf lorsque qu’expressément mentionné autrement dans un énoncé ou autorisé par le professeur.

Premier démarrage

Insérez la carte MicroSD avec l’image du cours dans la fente prévue à cet effet sur le Raspberry Pi. Branchez un écran (une sortie HDMI est disponible, n’oubliez pas d’utiliser le convertisseur mini-HDMI vers HDMI) ainsi qu’un clavier (utilisez la prise USB la plus proche du port HDMI pour brancher le clavier et la plus éloignée pour l’alimentation). Vous devrez d’abord vous authentifier avec le compte par défaut :

  • Nom d’utilisateur : pi
  • Mot de passe : gif3004

Si tout s’est bien passé, vous devriez vous retrouver face à un écran vous demandant de changer votre mot de passe. Le mot de passe par défaut de l’image est “gif3004”, nous vous recommandons fortement de le remplacer par un mot de passe plus sécuritaire (et personnel).

Vous devriez trouver la configuration suivante dans /etc/wpa_supplicant/wpa_supplicant.conf:

country=CA
ctrl_interface=/var/run/wpa_supplicant GROUP=netdev
network={
    ssid="eduroam"
    auth_alg=OPEN
    key_mgmt=WPA-EAP
    proto=WPA2
    eap=PEAP
    phase1="peaplabel=0"
    phase2="auth=MSCHAPV2"
    identity="IDUL@ulaval.ca"
    password="MOTDEPASSE"
}

Si elle est absente, veuillez l’ajouter. Lors du premier démarrage, éditez les éléments suivants du fichier pour connectez le Raspberry Pi au réseau eduroam:

  • Dans le champ identity, écrivez votre IDUL@ulaval.ca
  • Dans le champ password, écrivez votre NIP

Par la suite, redémarrez le Raspberry Pi et vérifiez que vous pouvez vous connecter à distance via SSH. Nous vous suggérons de mettre en place une authentification par clé publique, pour vous éviter de devoir réécrire le même mot de passe à chaque connexion :

# L'étape suivante est à effectuer sur votre machine (PAS le Raspberry Pi) et n'est nécessaire que si vous n'avez pas déjà de clé SSH
$ ssh-keygen -t rsa -b 4096 -C "ecrivez_votre_nom_ici"
# Pressez 3 fois sur Enter (les choix par défaut sont bons)

# Cette étape est commune à toutes les installations, mais assurer-vous d'utiliser ici la bonne adresse
$ ssh-copy-id pi@adresse_ip_de_votre_raspberry_pi

Nous recommandons finalement l’installation et l’utilisation d’un résolveur DNS tel que DuckDNS (gratuit), qui vous permettra de vous connecter plus facilement à votre Raspberry Pi en vous permettant d’utiliser un nom de domaine tel que “tarteauxframboises.duckdns.org” plutôt qu’une adresse IP pouvant potentiellement varier au fil de la session et de l’endroit où vous êtes.

Installation de la machine virtuelle de développement

Ce cours requiert l’utilisation d’un système GNU/Linux. Dans le cadre du cours, vous avez trois options :

  • Utiliser un des ordinateurs du laboratoire informatique 0105, sur lesquels les logiciels et outils nécessaires au cours sont pré-installés;
  • Télécharger une machine virtuelle VirtualBox à l’adresse suivante – le nom d’utilisateur est setr et le mot de passe gif3004, vous n’avez pas accès à la commande sudo, mais pouvez passer en mode root en utilisant su;
  • Utiliser votre propre installation Linux, notez que nous ne pouvons dans ce cas garantir que les étapes d’installation et de configuration seront exactement les mêmes.

Installation de l’environnement de compilation croisée

Le Raspberry Pi possède un processeur dont l’architecture (ARM) diffère de celle de votre ordinateur (x86-64). Vous ne pouvez donc pas directement transférer un exécutable compilé sur votre ordinateur. Il faut plutôt utiliser un environnement de compilation croisée, qui permettra à votre ordinateur de générer des binaires compatibles avec l’architecture ARM du Raspberry Pi. Pour mettre en place cet environnement, nous devrons (dans l’ordre) :

  1. Installer Crosstool-NG, un outil nous permettant de créer des chaînes de compilation croisée;
  2. Configurer Crosstool-NG selon les spécificités du Raspberry Pi Zero;
  3. Compiler et installer l’environnement de compilation croisée sur votre ordinateur;
  4. Synchroniser les librairies et en-têtes depuis le Raspberry Pi Zero;
  5. Préparer une configuration CMake pour la compilation croisée.

Notez que la compilation de cet environnement peut prendre un certain temps.

1) Installation de Crosstool-NG

Pour installer Crosstool-NG, récupérez d’abord la version utilisée dans le cours, puis exécutez le script bootstrap :

$ git clone ssh://votre_idul@pika.gel.ulaval.ca/depots/2020/h/GIF3004/crosstool-ng
$ cd crosstool-ng
$ ./bootstrap

Dans ce même répertoire, utilisez ./configure pour préparer la compilation et make pour le compiler :

$ ./configure --prefix=$HOME/crosstool-install
$ make && make install

Le paramètre prefix indique l’endroit où les outils de Crosstool-NG doivent être installés. Vous devrez également ajouter ce chemin d’installation dans votre variable d’environnement PATH.

Note : il se peut que l’étape du configure échoue si vous effectuez l’installation sur votre ordinateur (sans utiliser la machine virtuelle du cours). Assurez-vous dans ce cas d’avoir installé toutes les dépendances de Crosstool-NG. Cette étape a déjà été effectuée pour vous sur les ordinateurs du lab ou avec la machine virtuelle fournie.

2) Configuration de l’environnement de compilation croisée

Nous allons maintenant préparer la compilation de l’environnement de compilation croisée (oui, c’est méta). Pour ce faire, Crosstool-NG a besoin d’informations sur notre système cible (le Raspberry Pi). Créez tout d’abord un dossier nommé ct-config-rpi-zero dans votre dossier personnel et allez à l’intérieur :

$ mkdir ct-config-rpi-zero
$ cd ct-config-rpi-zero

Au lieu de partir d’une configuration vide, nous allons utiliser le fichier de configuration fourni par le distributeur des Raspberry Pi. Dans le dossier ct-config-rpi-zero, téléchargez le fichier suivant et nommez le .config :

$ wget -O .config wcours.gel.ulaval.ca/GIF3004/.config

Par la suite, lancez l’utilitaire de configuration de Crosstool-NG :

$ ct-ng menuconfig

Vous devriez alors obtenir une interface de ce type :

Important : suivez scrupuleusement les instructions suivantes. Tout manquement risque d’entraîner des erreurs ultérieures difficiles à interpréter et à corriger.

Allez dans la section Paths and misc options et remplacez :

  • Prefix directory : ${HOME}/arm-cross-comp-env/${CT_TARGET} (il est important de le faire, car les scripts de compilation fournis assument ce chemin précis)
  • Si vous utilisez un ordinateur du 0105 : Sur ces ordinateurs, il faut utiliser un dossier temporaire dédié et non pas l’espace de votre compte. Dans l’option Working directory. Remplacez donc ${CT_TOP_DIR}/.build par /gif3004/.build.
  • Log to a file (tout en bas): désactivez l’option

À gauche, la configuration requise sur votre machine virtuelle ou votre ordinateur. À droite, la configuration requise sur un ordinateur du 0105.

Dans la section Operating System, remplacez :

  • Source of linux : Custom location
  • Une fois l’étape précédente effectuée, Custom location : chemin vers les sources du kernel

Dans la dernière étape, chemin vers les sources du kernel doit être le chemin absolu vers le dossier contenant les sources du noyau Linux utilisé sur le Raspberry Pi. Celui-ci peut-être situé à des endroits différents selon votre installation:

  • Si vous travaillez sur les ordinateurs du laboratoire 0105, le chemin est /opt/rPi/linux-rpi-4.19.y-rt
  • Si vous travaillez sur la machine virtuelle Fedora fournie, le chemin est /home/setr/rPi/linux-rpi-4.19.y-rt
  • Si vous travaillez sur votre propre ordinateur, téléchargez l’archive suivante, décompressez-la et indiquez son chemin absolu.

Dans la section Debug facilities :

  • Activez gdb et strace
  • Allez ensuite dans les options de configuration de gdb (la touche Espace active ou désactive, la touche Entrée permet d’entrer dans les options) et désactivez l’élément Enable python scripting

N’oubliez pas d’enregistrer votre configuration (utilisez les flèches horizontales du clavier pour vous déplacer dans le menu du bas) puis quittez l’utilitaire.

3) Compilation et installation de la chaîne de compilation

Utilisez la commande suivante pour lancer la compilation :

$ ct-ng build

Cette compilation peut prendre un bon moment (comptez au moins 30 minutes), dépendant de la puissance de votre ordinateur. Si vous utilisez une machine virtuelle, pensez à augmenter le nombre de processeurs alloués à celle-ci, puisque Crosstool-NG peut en tirer parti. Vous aurez également besoin d’une bonne connexion Internet.

Une fois cela fait, le répertoire ~/arm-cross-comp-env devrait contenir un dossier nommé arm-raspbian-linux-gnueabihf. Dans ce dossier, vous retrouverez plusieurs choses, mais en particulier :

  • bin/, qui contient des exécutables x86-64 capables de générer du code machine ARM. Assurez-vous que ce dossier soit dans votre chemin d’exécution (PATH). Lorsque nous voudrons compiler un programme vers un binaire ARM, nous n’utiliserons donc pas gcc (qui compilerait en x86-64), mais bien arm-raspbian-linux-gnueabihf-gcc
  • arm-raspbian-linux-gnueabihf/sysroot, qui contient les librairies et en-têtes des librairies centrales au système (libc, binutils, etc.). C’est là que le compilateur et l’éditeur de liens iront chercher les informations dont ils ont besoin.

4) Synchronisation avec le Raspberry Pi

Important : attendez que l’étape précédente soit terminée sans erreurs avant de continuer.

À ce stade, vous êtes en possession d’une chaîne de compilation croisée. Il vous faut toutefois maintenant la synchroniser avec le Raspberry Pi, de manière à vous assurer que les versions des librairies et des en-têtes soient les mêmes lorsque vous compilerez (sur votre ordinateur) et exécuterez (sur le Raspberry Pi). Il va falloir synchroniser trois répertoires :

  • /usr/lib et /lib, qui contiennent les librairies partagées (fichier .so) qui peuvent être utilisées par les programmes;
  • /usr/include, qui contient les en-têtes de ces librairies (nécessaires pour la compilation);
  • /opt, qui contient certains fichiers de configuration importants.

Pour synchroniser ces dossiers, nous allons utiliser rsync. Cet outil permet de faire des mises à jour incrémentales, c’est-à-dire que seules les différences sont transférées.

$ cd ~/arm-cross-comp-env/arm-raspbian-linux-gnueabihf/arm-raspbian-linux-gnueabihf
$ rsync -av --numeric-ids --exclude "*.ko" --exclude "*.fw" --exclude "/opt/vc/src" --delete pi@adresse_ip_ou_nom_dhote:{/lib,/opt} sysroot
$ rsync -av --numeric-ids --exclude "/usr/lib/.debug" --delete pi@adresse_ip_ou_nom_dhote:{/usr/lib,/usr/include} sysroot/usr

Il reste par la suite un petit problème à corriger. Beaucoup de fichiers sont en fait des liens, qui évitent de devoir stocker deux fois le même fichier inutilement. Toutefois, certains de ces liens sont absolus, c’est-à-dire qu’ils contiennent un chemin absolu. Vous pouvez constater ce problème en testant, par exemple :

$ ls -l sysroot/usr/lib/arm-linux-gnueabihf/libdl.so 
lrwxrwxrwx 1 setr setr 35 15 jun  2017 sysroot/usr/lib/arm-linux-gnueabihf/libdl.so -> /lib/arm-linux-gnueabihf/libdl.so.2

Comme on le voit, le lien pointe vers un chemin absolu, qui n’existe pas sur notre plateforme de compilation. Il y a plusieurs solutions pour corriger ce problème, vous pouvez consulter cette page pour en savoir plus, mais le plus simple est d’utiliser la commande suivante. Attention, le find doit être exécuté dans le répertoire sysroot, sinon les chemins ne seront pas convertis correctement!

$ cd ~/arm-cross-comp-env/arm-raspbian-linux-gnueabihf/arm-raspbian-linux-gnueabihf/sysroot
$ find . -lname '/*' | while read l ; do   echo ln -sf $(echo $(echo $l | sed 's|/[^/]*|/..|g')$(readlink $l) | sed 's/.....//') $l; done | sh

Vous devrez effectuer cette synchronisation à chaque fois que vous ajouterez une librairie ou mettrez à jour votre système sur le Raspberry Pi.

5) Préparation d’une configuration CMake

CMake est un outil permettant de mettre en place une chaîne de compilation efficace et portable. Nous allons l’utiliser dans le cadre du cours afin d’automatiser la compilation et l’édition de liens des TP. Pour ce faire, créez un nouveau fichier dans arm-cross-comp-env/, nommé rpi-zero-w-toolchain.cmake et insérez-y le contenu suivant :

# Identification du systeme cible
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_VERSION 4.19)
set(CMAKE_SYSTEM_PROCESSOR arm)

# Localisation du sysroot
set(CMAKE_SYSROOT $ENV{HOME}/arm-cross-comp-env/arm-raspbian-linux-gnueabihf/arm-raspbian-linux-gnueabihf/sysroot)

# Selection du compilateur
set(tools $ENV{HOME}/arm-cross-comp-env/arm-raspbian-linux-gnueabihf)
set(CMAKE_C_COMPILER ${tools}/bin/arm-raspbian-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER ${tools}/bin/arm-raspbian-linux-gnueabihf-g++)

# On ajoute des options au compilateur pour lui indiquer ou aller chercher les librairies
SET(FLAGS "-Wl,-rpath-link,${CMAKE_SYSROOT}/opt/vc/lib -Wl,-rpath-link,${CMAKE_SYSROOT}/lib/arm-linux-gnueabihf -Wl,-rpath-link,${CMAKE_SYSROOT}/usr/lib/arm-linux-gnueabihf -Wl,-rpath-link,${CMAKE_SYSROOT}/usr/local/lib")
SET(CMAKE_CXX_FLAGS ${FLAGS} CACHE STRING "" FORCE)
SET(CMAKE_C_FLAGS ${FLAGS} CACHE STRING "" FORCE)

# Quoi aller chercher dans la sysroot (on ne veut pas aller chercher les programmes puisqu'ils sont
# compiles en ARM et ne peuvent donc etre directement executes sur un processeur x86)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

Nous réutiliserons cette configuration générique pour tous les projets du cours. Nous verrons plus loin comment la lier aux dits projets.

Configuration du projet du TP1

Dans le cadre du cours, nous allons utiliser Visual Studio Code (ci-après abbrévié VSC). Utilisez la version 1.19 comme les versions plus récentes causent certaines complications rendant la configuration initiale plus ardue. Vous êtes libres d’utiliser un autre environnement de développement, mais vous devez travailler en compilation croisée et nous ne pourrons potentiellement pas vous aider si vous choisissez un autre logiciel.

Installer les extensions requises

Une fois VSC ouvert, sélectionnez l’interface de recherche des extensions en cliquant sur la cinquième icône dans la barre de gauche. Par la suite, recherchez l’extension “C/C++” et installez le premier résultat.

Afin d’être utilisée, l’extension doit maintenant recharger l’interface de VSC, cliquez sur le bouton Reload pour le faire.

Vous devez installer les extensions suivantes : C/C++, CMake, CMake Tools et Native Debug.

Si vous utilisez l’image VirtualBox fournie, ces extensions devraient déjà être installées.

Création d’un nouveau projet

Sur VSC, les projets sont simplement des dossiers. Créez donc dans votre dossier personnel un nouveau dossier nommé projets puis, dans celui-ci, clonez le dépôt Git suivant :

$ git clone https://GIF3004@bitbucket.org/GIF3004/laboratoire1.git

Rendez également le script syncAndStartGDB.sh exécutable :

$ chmod +x syncAndStartGDB.sh

Par la suite, dans VSC, allez dans Fichier > Ouvrir un dossier et sélectionnez laboratoire1. Vous devriez alors pouvoir accéder, via le menu de gauche, aux fichiers tp1.c et CMakeLists.txt.

Compilation croisée

Il est maintenant temps de tester votre chaîne de compilation croisée. Dans VSC, allez dans le menu Afficher, puis Palette de commandes.

Cette palette de commandes est la manière privilégiée d’interagir avec les outils de VSC. Dans la suite des énoncés, nous l’appelerons simplement “Palette”. Vous gagnerez probablement du temps à mémoriser le raccourci clavier permettant de l’ouvrir!

Dans la ligne d’édition qui apparaît en haut de l’écran, écrivez CMake (remarquez comment VSC modifie ses suggestions au fur et à mesure), puis sélectionnez CMake Build. VSC vous demandera alors de choisir entre Debug, Release, MinSizeRel et RelWithDebInfo. Pour le moment, sélectionnez Debug, mais sachez que Release pourra être fort utile lorsque vous aurez besoin du maximum de performance possible. Notez que vous pouvez également utiliser la touche F7 comme raccourci.

Exécution et débogage

Si la compilation se termine avec succès, vous pouvez maintenant passer à l’étape de l’exécution du programme. Ici, nous cherchons à exécuter le programme sur le Raspberry Pi, mais en vous permettant de voir sa sortie et de le contrôler depuis votre ordinateur. Nous vous fournissons des scripts permettant de configurer VSC à cet effet. Vous devez cependant préalablement configurer un paramètre important. Dans le fichier .vscode/tasks.json, remplacez adresse_de_votre_raspberry_pi par l’adresse (IP ou DNS) effective de votre Raspberry Pi. Faites de même dans le fichier .vscode/launch.json, en conservant toutefois le :4567 qui suit l’adresse du Raspberry Pi.

Une fois cela fait, vous pouvez synchroniser l’exécutable et lancer le débogage en allant dans le menu Déboguer puis Lancer le débogage (la touche F5 est un raccourci plus rapide ayant le même effet). Après quelques secondes (le script utilise rsync pour synchroniser les fichiers vers le Raspberry Pi), l’interface de débogage devrait s’afficher et vous permettre de déboguer le programme à distance.

Correction des bogues

À ce stade, vous devriez être en mesure de lancer une session de débogage à distance sur le Raspberry Pi. Il est maintenant temps d’utiliser tout cela à bon escient! Le fichier qui vous est fourni contient trois erreurs distinctes en plus de générer plusieurs avertissements de la part du compilateur. Ces erreurs ne sont pas des erreurs de compilation, mais des erreurs de logique, qui empêchent le programme d’avoir le bon comportement. Vous devez les identifier et les corriger en utilisant le débogueur de VSC. Vous devez également pouvoir expliquer leur cause, de même que les corrections à apporter pour que le programme fonctionne correctement.

Finalement, vous devez corriger le code de manière à ce que GCC ne renvoie plus aucun warning lors de la compilation (tout en conservant le fonctionnement du programme, bien entendu). Prenez l’habitude de lire et décortiquer les avertissements du compilateur; ceux-ci révèlent parfois des erreurs cachées (et c’est le cas ici…).

Modalités d’évaluation

Ce travail est individuel. Aucun rapport n’est à remettre, mais vous devez être en mesure de démontrer que votre environnement de développement est fonctionnel et que vous savez utiliser ses fonctions basiques. Cette évaluation sera faite lors des séances de laboratoire du 25 janvier 2019 et du 1er février 2019. Ce travail compte pour 5% de la note totale du cours.

Le barême d’évaluation détaillé sera le suivant (laboratoire noté sur 20 pts) :

  • (4 pts) Raspberry Pi fonctionnel, y compris à distance (via SSH);
  • (6 pts) Chaîne de compilation croisée correctement construite et installée dans $HOME/arm-cross-comp-env, capacité à produire un binaire ARM;
  • (2 pts) Visual Studio Code installé et fonctionnel, débogage à distance utilisable;
  • (6 pts) Programme débogué : le programme doit s’exécuter sans erreur et produire un résultat correct. L’étudiant doit pouvoir expliquer les raisons des erreurs dans le programme initial;
  • (2 pts) Programme corrigé : le programme doit pouvoir être compilé sans générer aucun warning et ce en produisant toujours un résultat correct.

Ressources et lectures connexes