Xenomai sur Raspberry Pi 3 : bon espoir mais bilan mitigé

Publié par cpb
Mar 20 2017

Depuis plusieurs mois, on m’interroge régulièrement sur le support de Xenomai sur Raspberry Pi 3 tant durant mes sessions de formations sur Xenomai que dans des messages par mail. J’avais l’habitude de répondre que Xenomai ne fonctionnait pas encore sur ce modèle de Raspberry Pi.

Une remarque récente de Syrine dans les commentaires de l’article “Xenomai 3 sur Raspberry Pi 2” a attiré mon attention sur un site japonais qui utilise les mêmes commandes que celles présentées dans cet article et fait fonctionner l’ensemble sur un Raspberry Pi 3. J’ai voulu tenter également cette installation. Le résultat est un peu mitigé : Xenomai fonctionne parfaitement avec une bonne stabilité, sauf en ce qui concerne la gestion des interruptions des GPIO qui n’est pas supportée encore. Si vous êtes néanmoins tentés par l’expérience, voici comment procéder simplement.

Compilation et installation

Dans le cadre d’une mise en œuvre professionnelle pour un client je travaillerais en cross-compilation sur une machine de développement de type PC pour produire l’image qui serait installée ensuite sur la cible. Pour ce petit article, j’ai préféré simplifier la mise en œuvre et réaliser une compilation native directement sur le Raspberry Pi 3. Le temps de construction est plus long, mais il reste très raisonnable (2h tout compris pour cette expérience).

Tout d’abord, installons une image de la distribution Raspbian toute neuve. N’utilisez pas la distribution Noob, l’organisation des partitions est différente.

[~]$ sudo dd if=2017-02-16-raspbian-jessie-lite.img of=/dev/sdc bs=4M

Nous démarrons à présent le Raspberry pi 3 sur cette nouvelle distribution. Elle va rebooter automatiquement pour agrandir la partition et occuper tout la carte SD. Connectons-nous

raspberrypi login: pi
Password: raspberry

Il nous faudra quelques outils supplémentaires à ajouter à la distribution d’origine :

pi@raspberrypi:~$ sudo apt install -y git ncurses-dev bc autoconf libtool

Téléchargeons les sources de Xenomai. Je prends la dernière version disponible à ce jour :

pi@raspberrypi:~$ wget http://xenomai.org/downloads/xenomai/stable/xenomai-3.0.3.tar.bz2
pi@raspberrypi:~$ tar xjf xenomai-3.0.3.tar.bz2
pi@raspberrypi:~$ ls xenomai-3.0.3/kernel/cobalt/arch/arm/patches/
ipipe-core-3.10.32-arm-13.patch  ipipe-core-3.18.20-arm-9.patch  README
ipipe-core-3.14.44-arm-16.patch  ipipe-core-4.1.18-arm-4.patch

Le patch ipipe le plus récent proposé par Xenomai s’applique sur un noyau Linux 4.1. Nous avons de la chance, il existe une branche 4.1 pour le kernel spécifiquement modifié pour Raspberry Pi. Téléchargeons cette branche. L’option --depth 1 raccourcit le temps de téléchargement (2 à 3 minutes) en limitant l’historique au seul dernier commit :

pi@raspberrypi:~$ git clone https://github.com/raspberrypi/linux.git -b rpi-4.1.y --depth 1
pi@raspberrypi:~$ head -4 linux/Makefile
VERSION = 4
PATCHLEVEL = 1
SUBLEVEL = 21
EXTRAVERSION =
pi@raspberrypi:~$

Le noyau est un 4.1.21. Il n’y a pas beaucoup de différences avec le 4.1.18, Xenomai fonctionnera. Néanmoins une option de configuration a été modifiée, et pour que le patch ipipe puisse s’applique nous devons le modifier. Pour cela, j’ai préparé un petit patch (un patch de patch, cela explique qu’il contienne un signe '+' décalé) que nous téléchargeons :

pi@raspberrypi:~$ wget https://www.blaess.fr/christophe/files/article-2017-03-20/001-adapt-4.1.18-patch-to-rpi-4.1.21-kernel.patch

Appliquons ce mini-patch à Xenomai.

pi@raspberrypi:~$ ls
001-adapt-4.1.18-patch-to-rpi-4.1.21-kernel.patch  linux  xenomai-3.0.3  xenomai-3.0.3.tar.bz2
pi@raspberrypi:~$ cd xenomai-3.0.3/
pi@raspberrypi:~/xenomai-3.0.3$ patch -p1 < ../001*
patching file kernel/cobalt/arch/arm/patches/ipipe-core-4.1.18-arm-8.patch
pi@raspberrypi:~/xenomai-3.0.3$

Nous pouvons maintenant appliquer le patch ipipe modifié au noyau Linux que nous avons téléchargé :

pi@raspberrypi:~/xenomai-3.0.3$ ./scripts/prepare-kernel.sh --arch=arm --linux=../linux --ipipe=kernel/cobalt/arch/arm/patches/ipipe-core-4.1.18-arm-8.patch

Il y a quelques messages indiquant un décalage entre le noyau standard et le noyau pour Raspberry Pi (offset xx lines) mais la commande patch arrive à résoudre ces problèmes (Hunk #x succeeded).

Un second patch va être nécessaire. Comme je l’indiquais dans cet article, il s’agit d’une gestion des timers spécifique au Raspberry Pi.

pi@raspberrypi:~/xenomai-3.0.3$ cd ..
pi@raspberrypi:~$ wget https://www.blaess.fr/christophe/files/article-2017-03-20/002-xenomai-3-on-bcm-2709.patch
pi@raspberrypi:~$ cd linux
pi@raspberrypi:~/linux$ patch -p1 < ../002*

Nous allons compiler ce noyau. Pour cela, j’ai préparé un fichier de configuration qui regroupe les options nécessaires. Il s’agit d’une configuration un peu allégée pour réduire le temps de compilation ; s’il vous manque des fonctionnalités, n’hésitez pas à l’éditer (avec make menuconfig) et la modifier :

pi@raspberrypi:~/linux$ wget https://www.blaess.fr/christophe/files/article-2017-03-20/linux.config
pi@raspberrypi:~/linux$ mv linux.config .config

Pour la compilation proprement dite, le Raspberry Pi 3 va être très sollicité, d’autant que nous occupons ses quatre cœurs au maximum (option -j 4 pour quatre jobs en parallèle).
Vu la chaleur qu’il dégage, je vous conseille de le placer de façon à ce que le processeur soit bien ventilé (pas de boîtier fermé). L’idéal consiste à placer sur le processeur un petit radiateur de dissipation comme celui-ci.

 

Pour un Raspberry Pi 3 dans un boîtier fermé sans dissipateur, j’aurais tendance à supprimer l’option -j 4 de la ligne suivante, afin de soulager la charge du processeur, au prix d’un temps de compilation  plus long.

pi@raspberrypi:~/linux$ time make -j 4
[...]
  IHEX2FW firmware/keyspan_pda/keyspan_pda.fw
  IHEX2FW firmware/keyspan_pda/xircom_pgs.fw

real    103m36.178s
user    277m51.420s
sys     13m39.250s
pi@raspberrypi:~/linux$ 
pi@raspberrypi:~/linux$

Comme vous le voyez, il faut environ 1h30 de compilation sur un Raspberry Pi 3 pour obtenir notre noyau. Ne soyez pas surpris de voir passer pendant la compilation des warnings indiquant du code douteux. Ils se produisent tous dans des modules spécifiques au Raspberry Pi ; c’est leur niveau inégal de qualité qui a empêché leur intégration dans le noyau Linux standard.

Nous allons installer ce nouveau kernel à côté de celui de la distribution Raspbian afin de pouvoir revenir en arrière en cas de problème :

pi@raspberrypi:~/ipipe$ sudo cp arch/arm/boot/zImage /boot/kernel-xenomai.img
pi@raspberrypi:~/ipipe$ sudo make modules_install
pi@raspberrypi:~/ipipe$ sudo make dtbs_install
pi@raspberrypi:~/ipipe$ ls /boot/dtbs/4.1.21-xenomai+/
bcm2709-rpi-2-b.dtb  bcm2710-rpi-3-b.dtb  overlays

Le nouveau noyau, ses modules et son device tree étant installés, nous devons modifier le fichier de configuration du bootloader pour lui indiquer quel kernel charger :

pi@raspberrypi:~/ipipe$ sudo nano /boot/config.txt

Ajoutez les deux lignes suivantes à la fin du fichier :

kernel=kernel-xenomai.img
device_tree=dtbs/4.1.21-xenomai+/bcm2710-rpi-3-b.dtb

Testons notre noyau incluant ipipe :

pi@raspberrypi:~/ipipe$ sudo reboot

Si le système ne redémarre pas, montez la carte SD sur un PC, mettez en commentaire les deux lignes ci-dessus (en les précédant d’un '#') pour redémarrer sur la distribution Raspbian initiale. Vérifiez bien l’emplacement du noyau, du device tree, et le contenu du fichier config.txt.

Le Raspberry Pi 3 ayant bien redémarré sur le noyau patché avec ipipe, nous pouvons vérifier sa présence ainsi

raspberrypi login: pi
Password: raspberry
[...]
pi@raspberrypi:~$ uname -a
Linux raspberrypi 4.1.21-xenomai+ #1 SMP PREEMPT Sat Mar 18 10:12:21 UTC 2017 armv7l GNU/Linux
pi@raspberrypi:~$ cat /proc/ipipe/version
8
pi@raspberrypi:~$ cat /proc/xenomai/version
3.0.3
pi@raspberrypi:~$

Le travail n’est pas terminé pour autant, nous devons compiler les bibliothèques et les outils de tests de Xenomai.

pi@raspberrypi:~$ cd xenomai-3.0.3/
pi@raspberrypi:~/xenomai-3.0.3$ ./scripts/bootstrap

Cette étape reste silencieuse durant quelques minutes. Laissez le script travailler, il n’est pas bloqué.

pi@raspberrypi:~/xenomai-3.0.3$ ./configure --enable-smp
pi@raspberrypi:~/xenomai-3.0.3$ make
pi@raspberrypi:~/xenomai-3.0.3$ sudo make install

Deux heures après le début de notre expérience, Xenomai est prêt à être utilisé. Le test le plus simple – et l’un des plus représentatifs – consiste à mesure les fluctuations d’un timer. Pour cela un outil est directement fourni. Je le lance en lui demandant de s’exécuter pendant 60 secondes :

pi@raspberrypi:~/xenomai-3.0.3$ sudo /usr/xenomai/bin/latency -T 60
== Sampling period: 1000 us
== Test mode: periodic user-mode task
== All results in microseconds
warming up...
RTT|  00:00:01  (periodic user-mode task, 1000 us period, priority 99)
RTH|----lat min|----lat avg|----lat max|-overrun|---msw|---lat best|--lat worst
RTD|     -3.594|     -2.786|      1.822|       0|     0|     -3.594|      1.822
RTD|     -3.647|     -2.923|      0.051|       0|     0|     -3.647|      1.822
RTD|     -3.595|     -2.920|      1.613|       0|     0|     -3.647|      1.822
RTD|     -3.648|     -2.946|     -0.106|       0|     0|     -3.648|      1.822
[...]
RTD|     -3.616|     -2.936|     -0.230|       0|     0|     -5.017|      1.863
RTD|     -3.616|     -2.871|      1.488|       0|     0|     -5.017|      1.863
RTD|     -3.669|     -2.848|      0.186|       0|     0|     -5.017|      1.863
---|-----------|-----------|-----------|--------|------|-------------------------
RTS|     -5.017|     -2.893|      1.863|       0|     0|    00:01:00/00:01:00
pi@raspberrypi:~/xenomai-3.0.3$

Ces premiers résultats sont intéressants, ce sont les deux dernières colonnes qui sont les plus significatives.

La dernière colonne indique la pire latence mesurée, c’est à dire le pire retard de déclenchement d’un timer périodique. La valeur de 1,863 microsecondes est excellente, mais le système est au repos et ce n’est pas représentatif.
L’avant-dernière valeur montre la meilleure latence observée. Elle est négative (-5,017 microsecondes), ce qui indique que le timer s’est déclenché plus tôt que prévu. En effet, Xenomai anticipe les retards inhérents au matériel en déclenchant les timers un peu en avance. Ceci est intéressant pour avoir une bonne précision, mais nécessite une calibration. Nous pouvons consulter l’avance par défaut :

pi@raspberrypi:~/xenomai-3.0.3$ cat /proc/xenomai/latency 
10052

La valeur est de 10052 nanosecondes soit 10,052 microsecondes. Tous les timers sont donc anticipés de 10 microsecondes environ. Ceci peut être intéressant en production, mais dans un cadre expérimental, ce sont les vraies valeurs de latences qui m’intéressent. Je désactive donc cette avance :

pi@raspberrypi:~/xenomai-3.0.3$ sudo -i
root@raspberrypi:~# echo 0 > /proc/xenomai/latency 
root@raspberrypi:~# cat /proc/xenomai/latency 
0
root@raspberrypi:~# exit

Je relance la mesure pendant une minute pour vérifier :

[...]
RTD|      6.385|      7.073|      9.979|       0|     0|      5.405|     35.980
RTD|      6.437|      7.077|     10.499|       0|     0|      5.405|     35.980
RTD|      6.436|      7.074|      9.561|       0|     0|      5.405|     35.980
RTD|      6.384|      7.083|      9.874|       0|     0|      5.405|     35.980
---|-----------|-----------|-----------|--------|------|-------------------------
RTS|      5.405|      7.142|     35.980|       0|     0|    00:01:00/00:01:00

Presque 36 microsecondes de latence, voilà qui est plus réaliste (et très bien, notons-le).

Je vais lancer une nouvelle mesure pendant une heure en conservant un histogramme des latences observées :

pi@raspberrypi:~$ sudo /usr/xenomai/bin/latency  -T 3600 -g histo-01.plot
[...]
RTD|      6.492|      7.145|      9.825|       0|     0|      4.229|     36.862
RTD|      6.439|      7.146|      9.877|       0|     0|      4.229|     36.862
RTD|      6.491|      7.166|      9.877|       0|     0|      4.229|     36.862
HSH|--param|--samples-|--average--|---stddev--
HSS|    min|      3600|      5.972|      0.205
HSS|    avg|   3600017|      6.586|      0.870
HSS|    max|      3600|      9.651|      1.783
---|-----------|-----------|-----------|--------|------|-------------------------
RTS|      4.229|      7.199|     36.862|       0|     0|    01:00:00/01:00:00
pi@raspberrypi:~/xenomai-3.0.3$

L’histogramme peut être dessiné par Gnuplot. En abscisse la latence observée en microsecondes et en ordonnée le nombre d’occurrences de chaque classe. Le résultat est le suivant :

Xenomai latency - Low system load

Il est intéressant de voir que très peu d’occurrences se produisent au-delà de 10 microsecondes. Pour avoir un aperçu plus précis sur ce qui se passe au-delà de 13 microsecondes, il est préférable de faire une représentation logarithmique en ordonnée qui va dilater et rendre plus visibles les faibles occurrences :

Xenomai latency - Low system load

La mesure a été faite alors que le système était au repos, ce qui ne reflète pas un comportement en production. Je relance la mesure en ajoutant une très forte charge du système avec le script dohell fourni avec Xenomai :

pi@raspberrypi:~$ sudo /usr/xenomai/bin/dohell 3600 & sudo /usr/xenomai/bin/latency  -T 3600 -g histo-02.plot
RTD|      7.690|     14.418|     49.044|       0|     0|      5.644|     62.846
RTD|      7.637|     14.028|     37.637|       0|     0|      5.644|     62.846
RTD|      7.585|     14.297|     44.356|       0|     0|      5.644|     62.846
RTD|      8.261|     14.237|     33.939|       0|     0|      5.644|     62.846
HSH|--param|--samples-|--average--|---stddev--
HSS|    min|      3600|      7.507|      0.799
HSS|    avg|   3600006|     14.871|      5.938
HSS|    max|      3600|     41.476|      4.462
---|-----------|-----------|-----------|--------|------|-------------------------
RTS|      5.644|     15.366|     62.846|       0|     0|    01:00:00/01:00:00

En outre, des rafales de ping en mode flood provenaient régulièrement d’un PC, produisant de nombreuses interruptions réseau.

Xenomai latency - High system load

Nous voyons donc qu’il y a une latence intrinsèque de l’ordre de 4.2 microsecondes au repos, et qu’en fonction de la charge du système elle peut aller jusqu’à une soixantaine de microsecondes. Ces résultats sont tout à fait cohérents avec ceux que l’on peut obtenir avec Xenomai sur d’autres cartes à processeur ARM et correspondent bien à ce que l’on attend d’un système d’exploitation complet comme Linux sous des contraintes temps réel fortes.

J’ai également fait l’essai d’ajouter sur la ligne de commande du kernel les options dwc_otg.fiq_enable=0 et dwc_otg.fiq_fsm_enable=0 afin d’éviter l’utilisation des Fast Interrupt Request qui perturbent le déterminisme du traitement des autres interruptions par Xenomai. Cela n’a pas changé les performances observées.

Conclusion

Xenomai 3.0.3 s’installe bien sur un Raspberry Pi 3. La gestion des timers est précise et présente peu de latences, même sous une forte charge système et interruptive.

Toutefois, il y a un point très décevant : je ne suis pas arrivé à gérer (avec l’API RTDM) les handlers d’interruptions des GPIO. Je suppose que le patch ipipe n’a pas encore été ajusté pour le Raspberry Pi 3. Il va nous falloir patienter encore un peu, je continuerai à utiliser des Raspberry Pi 2 pour les exemples de mes formations sur le temps réel.

14 Réponses

  1. joe dit :

    Bonjour,

    Est-ce que par “sauf en ce qui concerne la gestion des interruptions des GPIO qui n’est pas supportée encore.” vous voulez dire que toute forme d’interruption n’est pas possible? Par exemple un module externe qui genere une interruption?

  2. Harco Kuppens dit :

    you said: (translated in english)

    However, there is one very disappointing point: I have not managed to manage (with the RTDM API) the GPIO interrupt handlers . I guess the patch ipipe has not yet been adjusted for the Raspberry Pi 3. We will have to wait a little longer, I will continue to use Raspberry Pi 2

    Does this mean that you did get the GPIO interrupt handlers working for the Raspberry Pi 2?
    In your RPI2 article I couldn’t find any references to GPIO?

    • cpb dit :

      Yes, the GPIO handling works with Raspberry Pi 2 and RTDM.
      I’ll try to find an example tomorrow.

      • Harco Kuppens dit :

        I tried to get the rpi2 and RTDM working, however I didn’t succeeded.
        I managed to read and write gpio pins from realtime, but everytime I try to trigger an interrupt my pi hangs. I don’t understand what is going wrong???

        Could send me an gpio interrupt example which works on the rpi2?
        It would really help me!

        Much thanks for your this fine page, it already helped a lot!!

        best regards,
        Harco Kuppens

  3. Anurup dit :

    Hi,

    I’m trying to compile a certain code, https://github.com/severinson/VLC-driver after successfully compiling a xenomai-3 based kernel on a raspberry pi 3 as in your article.

    My issue is that I believe the code is on an older version of xenomai ( i was able to compile it using xenomai 2.6.3 to be exact). But the raspberry pi 3 needs a newer version of the kernel ( v 4 + ) to boot.

    I wasn’t able to find any articles regarding compiling xenomai v2 with linux kernel v4 for raspberry pi 3. If you can guide me on this I would be grateful.

  4. Audrey Queudet dit :

    Bonjour,

    Merci pour cette procédure d’installation détaillée qui m’a été très utile.

    Cependant, j’ai plusieurs erreurs lors de phase de compilation de Xenomai que je n’arrive pas à résoudre :
    #make
    xenomai-3.0.3/lib/cobalt/arch/arm/include/asm/xenomai/features.h:51:2: error: #error “Could not find current ARM architecture”
    #error “Could not find current ARM architecture”
    ^
    /home/queudet-a/Documents/Enseignements-Recherche/Enseignements_2017-2018/EMN/TP/xenomai-3.0.3/lib/cobalt/arch/arm/include/asm/xenomai/features.h:55:2: error: #error “SMP not supported below armv6, compile with -march=armv6 or above”
    #error “SMP not supported below armv6, compile with -march=armv6 or above”
    ^
    features.c: In function ‘cobalt_check_features’:
    features.c:63:3: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]
    (__xn_rdtsc_t *)(0xffff1004 –
    ^
    cc1: all warnings being treated as errors
    Makefile:486 : la recette pour la cible « libarch_la-features.lo » a échouée

    Pourriez-vous m’aider svp ? Pour information, j’utilise la chaîne de compilation croisée Buildroot pour laquelle j’ai lancé au départ un make raspberrypi3_defconfig.

    D’avance merci,
    Audrey Queudet

    • Robin 'BoB' Duprat dit :

      Il te faut spécifier l’architecture cible à GCC. Cela ce fait via les Makefiles, eux-même configuré par un autotools.

      => Ce qui implique que tu dois ajouter l’option suivante (pour du 32 bits) lors de l’éxecution du ./configure, soit :

      (*1) CFLAGS=’-march=armv7-a -mfpu=vfp3′

      Dans Buildroot (version 2017-02) tu peux y parvenir en faisant :
      – $ make menuconfig
      – Target Package > Real-Time > Additional configure options = (*1)

  5. Endy dit :

    Quelqu’un sait s’il y a du nouveau sur les interuptions du GPIO avec Xenomai + RPi 3 depuis ce post ? Je n’ai trouvé aucune information ailleurs.

    Merci!

  6. satish dit :

    Dear sir,

    I followed your installation steps of xenomai in raspberry pi 3 preinstalled with noobs but after the kernel compilation successfully, it does not reboot.

    pls assist me in solving the above issue.

    Regards,
    Satish D

  7. satish dit :

    Dear sir,

    I followed the above steps, but after reboot command, not rebooting. using raspbian jessy latest and raspberry pi 3.

    Kindly guide me.

    Regards,
    Satish D

  8. Corneliu Zuzu dit :

    Hi Christophe,

    I’m struggling to find a solution to a freeze for the last 2 weeks or so. My RbPi’s freeze when calling rt_task_set_affinity. Could you please try to run this simple code on yours:

    RT_TASK t;
    cpu_set_t s;

    rt_printf(“Doing dangerous stuff.\n”);
    rt_print_flush_buffers();
    rt_task_create(&t, NULL, 0, T_HIPRIO, T_JOINABLE);
    CPU_ZERO(&s);
    CPU_SET(2, &s);
    rt_task_set_affinity(&t, &s);
    rt_task_delete(&t);
    rt_printf(“Done dangerous stuff, goodbye.\n”);
    rt_print_flush_buffers();

    return 0;

    and tell me if you RbPi freezes entirely like mine does?

    Thank you,
    Corneliu.

  9. Corneliu Zuzu dit :

    I’ve just followed your steps precisely (I was using Raspbian desktop and cross-compiled both the kernel and Xenomai) and doing it _exactly_ as you describe here instead renders a system which does _NOT_ reproduce the freezing!

    Given that I’ve spent significant time trying to make this work I’ll slowly move towards my initial setup from this working one and try to identify the exact issue. Finally, I have a reasonable starting point to debug from :).

    Afterwards I’ll get back here and post what the actual problem behind the freeze was.

    Thank you,
    Corneliu.

  10. Corneliu Zuzu dit :

    Ok, so the problem was Xenomai 3.0.5. Apparently there’s a bug in this version.
    I actually expected this to be a problem with the kernel or the i-pipe patch, the issue lies only within the Xenomai user-land binaries (tested this by actually only installing 3.0.3 user binaries over a kernel which was already prepped with 3.0.5 version and it worked).

    GDB still freezes though if I try to step over in-between a call to rt_task_create() and rt_task_set_affinity() for that task, but this is acceptable for the moment since it only happens if GDB is attached.

    Maybe this will help someone.

    Cheers, Corneliu.

URL de trackback pour cette page