Linux Embedded

Le blog des technologies libres et embarquées

RTEMS sur Raspberry Pi

Nous avions évoqué la célèbre carte Raspberry Pi (RPi) lors d’un article en janvier 2013 concernant Linux et PREEMPT-RT. La RPi est effectivement prévue pour fonctionner sous Linux mais quelques contributeurs utilisent cette carte dans un autre environnement. Nous pouvons citer la page Bare-metal Raspberry Pi Programming sur http://en.wikibooks.org/wiki/Bare-metal_Raspberry_Pi_Programming. L’approche bare metal indique que l’on utilise le matériel sans système d’exploitation, le programme applicatif étant alors directement exécuté sur le matériel. Cette approche a permis le portage de systèmes d'exploitation légers comme FreeRTOS ou RTEMS sur la RPi
Le système d’exploitation RTEMS (Real Time Executive for Multiprocessor Systems) est un RTOS libre diffusé sous licence GPL. Il fut au départ créé dans les années 80 par l’armée américaine pour équiper les missiles (M pour Missile puis Military). Il a été « libéré » dans les années 90 puis placé sous licence GPL, le développement étant conduit par la société américaine OAR.
L’architecture de RTEMS est très différente de celle de Linux. Le système est constitué d’un noyau temps réel et l’application (unique) est liée statiquement à ce noyau. Les sources de RTEMS représentent environ 400000 lignes de C, ce qui est ridiculement faible par rapport à la somme des composants Linux (15 millions de lignes de code pour le seul noyau). La taille d’une application RTEMS (en fait le noyau et l’application dans un même fichier) représente donc quelques centaines (voire quelques dizaines) de Ko ce qui n’a rien à voir avec l’empreinte mémoire d’un système Linux.
RTEMS est très utilisé dans l’industrie spatiale aux USA mais également en Europe puisque l’ESA (European Space Agency) en a produit une version édulcorée et « validée ». En France, EADS Astrium utilise une version allégée pour ses satellites. Le CNES est également un utilisateur de RTEMS.
Au niveau matériel, RTEMS est souvent utilisé sur du matériel très coûteux à base de processeur SPARC LEON, qui a la particularité de bien résister aux radiations. Il est cependant intéressant de pouvoir tester RTEMS sur des systèmes plus courants et le système donc disponible sur x86, ARM, PowerPC ainsi que quelques architectures exotiques. Dans le numéro 6 de la revue Open Silicium (OS#6), nous avons décrit l’utilisation de RTEMS pour la carte ARM Mini2440, même si cette dernière est un peu désuète.
L’utilisation fréquente de la carte RPi dans l’enseignement puis dans l’industrie a conduit à la mise à jour du BSP officiel de RTEMS (version 4.11) afin d’ajouter le support de la RPi. Ce travail a été réalisé par un ingénieur de la NASA (Alan Cudmore, alan.p.cudmore@nasa.gov) visiblement sans le support officiel de cette prestigieuse organisation (It's taking longer, because I'm doing this at home, on my own time). Le BSP existant utilise la console série (UART) de la RPi, il est donc nécessaire de disposer d’un câble adéquat (USB/Série) comme celui proposé par Adafruit (voir la bibliographie)
Nous ne décrirons pas en détail la construction du compilateur croisé pour RTEMS et nous renvoyons le lecteur à l’article d’OS#6 déjà cité ou bien à la documentation RTEMS sur le wiki http://www.rtems.org/wiki/index.php/Main_Page. Si l’on part du principe qu’un compilateur croisé pour RTEMS existe sur le répertoire /opt/rtems-4.11_arm, on peut produire le BSP par la suite de commandes :

$ export PATH=/opt/rtems-4.11_arm/bin:$PATH
$ git clone git://git.rtems.org/rtems.git rtems
$ cd rtems
$ ./bootstrap
$ cd ..
$ mkdir b-rtems && cd b-rtems
$ ../rtems/configure --target=arm-rtems4.11 --disable-cxx --enable-tests=samples --enable-rtemsbsp=raspberrypi --prefix=/home/pierre/RTEMS/target_rpi
$ make

A l’issue de la compilation, un exécutable de test est disponible sur le répertoire arm-rtems4.11/c/raspberrypi/testsuites/samples/hello. Le fichier binaire à utiliser est hello.ralf et doit être copié en tant que kernel.img sur la partition VFAT de la carte SD insérée dans la RPi. Lors du redémarrage de la carte, on obtient alors l’affichage suivant.

*** HELLO WORLD TEST ***
Hello World
*** END OF HELLO WORLD TEST ***

Reconnaissons que le résultat n’est pas très spectaculaire ! Nous allons donc écrire un exemple original qui utilise des GPIO de la carte RPi (une entrée et une sortie). Le code complet de cet exemple est disponible sur https://github.com/pficheux/raspberry_pi. Le GPIO en sortie (numéro 16) correspond à la led ACT de la RPi située à coté du connecteur audio (bleu).
L’exemple utilise un pilote de périphérique très simple exploité par le programme principal.
Même si il en est très loin, RTEMS utilise certains concepts du système UNIX en particulier au niveau de la structure des pilotes :

  • Création d’un fichier spécial /dev/rpi_gpio
  • Utilisation d’appels système open(), read(), write(), ioctl(), close()

Le principe du programme est d’interroger périodiquement la carte via le pilote afin :

  1. de changer l’état du GPIO en sortie (led)
  2. de lire l’état du GPIO en entrée (activé par un bouton-poussoir)

Nous reproduisons ci-dessous un extrait de la partie principale du programme (fichier init.c) qui correspond à la fonction POSIX_Init() puisque nous avons choisi d’utiliser l'API de programmation POSIX disponible dans RTEMS.

void *POSIX_Init()
{
  puts( "\n\n*** RPi GPIO driver test ***" );

  if ((fd = open ("/dev/rpi_gpio", O_RDWR)) < 0) {
    fprintf (stderr, "open error => %d %s\n", errno, strerror(errno));
    exit (1);
  }

  // Configuration des GPIO (24 en entrée, 16 en sortie) 
  ioctl(fd, RPI_GPIO_IN, 24);
  ioctl(fd, RPI_GPIO_OUT, 16);

  ...
}

La fonction appelée périodiquement sur l’échéance d’un compteur POSIX est reproduite ci-dessous.

void got_signal (int sig)
{
  static int n = 0;
  int status, input;

  // Changement d’état sortie (GPIO 16 = led ACT)
  if (n % 2 == 0)
    status = ioctl (fd, RPI_GPIO_SET, 16);
  else
    status = ioctl (fd, RPI_GPIO_CLR, 16);

  // Lecture entrée (GPIO 24)
  if ((input = ioctl (fd, RPI_GPIO_READ, 24)) != gpio_input) {
    printf ("input= %d\n", ioctl (fd, RPI_GPIO_READ, 24));
    gpio_input = input;
  }

  n++;

  ...  
}

Le corps du pilote est situé dans le fichier rpi_gpio.c. Il faut noter qu’il n’y pas dans RTEMS de notion de module dynamique comme pour Linux. Le pilote sera donc compilé et lié de manière statique à l’ensemble (le noyau, les pilotes du système et l’application).

La première partie du pilote concerne l’accès physique aux GPIO. RTEMS n’utilisant pas de MMU, les adresses physiques sont directement utilisables (contrairement à Linux).

#include <rtems.h>
#include "rpi_gpio.h"

#define BCM2708_PERI_BASE    0x20000000
#define GPIO_BASE            (BCM2708_PERI_BASE + 0x200000) /* GPIO controler */

// GPIO setup macros
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))

#define GPIO_SET *(gpio+7)  // sets   bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
#define GPIO_READ(g) *(gpio+13) &= (1<<(g))

volatile unsigned int *gpio = (unsigned int *)GPIO_BASE;

La fonction suivante initialise le pilote et l’enregistre auprès du système en tant que fichier spécial /dev/rpi_gpio.

rtems_device_driver rpi_gpio_initialize(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void *pargp
)
{
  rtems_device_driver status;

  if ( !initialized ) {
    initialized = 1;

    status = rtems_io_register_name(
      "/dev/rpi_gpio",
      major,
      (rtems_device_minor_number) 0
    );

    if (status != RTEMS_SUCCESSFUL)
      rtems_fatal_error_occurred(status);
  }

  return RTEMS_SUCCESSFUL;
}

Le reste du pilote constitue les points d’entrée, soit open(), close(), ioctl() dans notre cas. Les fonctions open() et close() se résument à retourner un code d’état sans erreur.

rtems_device_driver rpi_gpio_open(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void *pargp
)
{
  return RTEMS_SUCCESSFUL;
}

rtems_device_driver rpi_gpio_close(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void *pargp
)
{
  return RTEMS_SUCCESSFUL;
}

Le point d’entrée suivant correspond à l’appel système ioctl(). Il utilise directement les fonctions/macros d’accès spécifiques à la RPi.

rtems_device_driver rpi_gpio_control(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void *pargp
)
{
  int n, cmd;
  rtems_libio_ioctl_args_t *args = pargp;

  n = (int)(args->buffer);
  cmd = (int)(args->command);
  switch (cmd) {
  case RPI_GPIO_SET : 
    GPIO_SET = 1 << n;
    break;

  case RPI_GPIO_CLR :
    GPIO_CLR = 1 << n;
    break;

  case RPI_GPIO_OUT :
    OUT_GPIO(n);
    break;

  case RPI_GPIO_IN :
    INP_GPIO(n);
    break;

  case RPI_GPIO_READ :
    args->ioctl_return = ((GPIO_READ(n) & (1 << n)) != 0);
    return RTEMS_SUCCESSFUL;

  default: 
    printk ("rpi_gpio_control: unknown cmd %x\n", cmd); 

    args->ioctl_return = -1;
    return RTEMS_UNSATISFIED;
  }
    
  args->ioctl_return = 0;

  return RTEMS_SUCCESSFUL;
}

La compilation de l’exemple utilise le fichier Makefile fourni avec les sources et compatible avec RTEMS. Il est nécessaire d’exporter la variable RTEMS_MAKEFILE_PATH afin d'indiquer le BSP à utiliser.

$ export RTEMS_MAKEFILE_PATH=$HOME/RTEMS/target_rpi/arm-rtems4.11/raspberrypi
$ make

Bibliographie
http://www.rtems.org
http://wiki.rtems.org/wiki/index.php/Main_Page
http://en.wikibooks.org/wiki/Bare-metal_Raspberry_Pi_Programming
http://alanstechnotes.blogspot.fr/2013/03/rtems-on-raspberry-pi.html
http://www.adafruit.com/products/954
http://ingenierie.openwide.fr/content/download/3672/29378/file/RTEMS_ASTRIUM_2012-1.pdf
Article RTEMS sur Mini2440 dans OS#6
https://github.com/pficheux/raspberry_pi
http://www.raspberrypi.org
Raccorder un bouton poussoir sur RPi

    • le 30 janvier 2014 à 17:23

      bonjour ,je veux embarquer un BSP Rtems sur la carte Zynq7000 ,est ce que ya quelqu’un qui a deja fait et qui a une idée sur le repository bsp-Rtems que je dois le rajouter dans l' EDK xilinix SVP ???

    • le 21 janvier 2015 à 14:07

      Bonjour Amine,

      Je suis etudiant à l'université de Bretagne Occidental, je suis en train d'essayer de porter un BSP Rtems sur la carte Zynq7000. Aviez-trouvé la solution à votre problème? Si oui comment aviez vous procédé?
      Cordialement.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.