Real-time programming with RTEMS

Frank Singhoff
Université of Brest



I. Installing the compilers and simulators



The material you need to do this set of exercises are available here:
  1. Directory RTEMS-SOFTWARES : contains the required software to do the exercises.
  2. Directory RTEMS-DOCS : contains a set of pdf files that are part of the RTEMS standard documentation (may help for the signature of each function you will need).
  3. Directory RTEMS-EXAMPLES : a set of RTEMS C/Ada examples.


Prior to compile any RTEMS program, we must put the RTEMS C cross-compiler in your $PATH shell variable. To do so, launch the following command in your working shell window:

source rtems.csh

provided you are using a TCSH shell. Notice that rtems.bash is also available if you are using BASH or ZSH instead.


II. Programming RTEMS with C/POSIX






Exercise 1 : Cross-compiling

In this exercise, we experiment the C cross-compiler for RTEMS. On Linux, we will generate programs that are able to run on SPARC/Leon processor architectures.
Download and save the following file and put it in a directory called EXO1.
  1. Compile this first example of C program with the following command:
    make clean
    make

    assuming you are in the EXO1 directory.
  2. If everything is OK, you should find a file named o-optimize/sched.exe.
  3. Launch the command file o-optimize/sched.exe : what is the meaning of the displayed information?
  4. Can we directly launch the binary o-optimize/sched.exe from your Unix Shell?
  5. Now, we will use the Leon processor simulator to run this program. This simulator is gracefully provided by the Gaisler Research company (See http://www.gaisler.com). To launch this simulator, call the following command from your Unix Shell:

    $tsim
    tsim>


  6. Now, load the binary into the memory of the simulator with the load command:

    tsim>load o-optimize/sched.exe


  7. Then, finally, run the program with the go command :

    tsim> go
    resuming at 0x40000000
    Max priority for SCHED_FIFO = 254
    Min priority for SCHED_FIFO = 1
    ...
  8. Answer to these questions:





Exercise 2 : Fixed priority/Rate Monotonic scheduling and periodic tasks



In this exercise, we experiment the fixed priority scheduling of RTEMS. Download and save the following file and put it in a directory called EXO2.


Question 1:





Question 2:


  • Now, we work with a new version of the init.c file to experiment a preemptive Rate Monotonic scheduling of periodic tasks. Download and save this new file in the EXO2 directory.
  • We assume a set of two periodic threads defined with the following parameters:

  • Draw by hand the scheduling sequence of this thread set on the hyper-period. We assume a preemptive Rate Monotonic scheduling. Do the thread meet their deadlines?
  • We now try to write a C program that is running this periodic thread set. For such a purpose, we must fill the init.c file in order to schedule this thread set according to Rate Monotonic. The sourcecode you have to fill is shown by several stars. Each thread T1 and T2 must run the function periodic_task. The period is sent to each thread throught the thread argument. In this exercise, you have three issues to solve:
    1. Assign and set threads priorities according to Rate Monotonic.
    2. Ensure that thread T1 and T2 will start synchronously : T1 and T2 must be activated for their first activation at the same time. The critical_instant variable stores the first release time for T1 and T2. To reach this critical instant, the initialization thread (thread running POSIX_init function) must not be preempted before its completion (when it reaches its pthread_exit() statement). To be sure that the initialization thread is completed without being preempted, you must assign a higher priority level to the thread running POSIX_init.
    3. Implement periodic release times. To implement that threads are released periodically, we need to use the nanosleep and the clock_gettime functions. Arithmetic operations on timespec structs can be made with C functions of files ts.h and ts.c. clock_gettime functions will allow to measure when a thread is completing its activation while nanosleep can be used to blocked the thread until it reaches its next periodic release.





    Exercise 3 : Synchronization : mutex vs counting semaphore




    Question 1:


    During this question, we experiment the synchronization tools provided by RTEMS. First, download and save the following file and put it in a directory called EXO3. This program contains two threads reading a text stored in a shared memory. The text is read character by character and is written to a second memory area.
    1. Compile and test the program. What can you see? Explain this behavior.
    2. Modify this program to correct this wrong behavior. For such a purpose, you can use the pthread_mutex_lock and pthread_mutex_unlock functions or the sem_wait and sem_post functions.



    Question 2:


    For this second question, download and save this new file and put it in the EXO3 directory.
    1. Compile and test this program. This program implements a producer/consumer synchronization. The buffer stores only one character. Producers write data in the buffer character by character. Consumers read data from the buffer character by character. The buffer is built with two counting semaphores called number_of_full_positions and number_of_empty_positions (respectivly initialized with the values 0 and 1). Those semaphores model/store the number of busy and empty positions into the buffer. These semaphores allow you to block producers when no empty locations exist in the buffer and respectively to block consumers when no full locations exist in the buffer.
      The algorithm of a producer is :
      while(1)
      {
         P(number_of_empty_positions);
         Produce and put a character into the buffer;
         V(number_of_full_positions);
      }
      


      The algorithm of a consumer is :
      while(1)
      {
         P(number_of_full_positions);
         Read and display a character from the buffer;
         V(number_of_empty_positions);
      }
      
    2. Change this program in order to define a buffer that is able to store upto 4 characters (e.g. a buffer composed of 4 positions). The buffer must be a FIFO buffer.
    3. Change your program in order to allow several producers and several consumers to handle your 4 positions buffer.

      Projet de CC UE SEE 2014/2015 : implantation d'un algorithme d'ordonnancement global multiprocesseur



      L'objectif du projet est d'implanter un logiciel pour simuler l'ordonnancement d'un jeu de tâches périodiques grâce à divers algorithmes d'ordonnancement. On vous donne une première version de ce logiciel. Ce logiciel est constitué de plusieurs composants. Le logiciel est disponible ici.

      Pour compiler et exécuter ce programme, vous devez utiliser un compilateur C et le fichier Makefile joint. Ce simulateur est constitué :
      1. Du fichier main.c qui montre comment utiliser le simulateur.
      2. Du simulateur a proprement dit qui est constitué des fichiers simulateur.h et simulateur.c.


      Travail à faire:

      Pour ce projet:
      • Vous pouvez travailler en groupe de 2 étudiants (pas plus).
      • Votre travail sera livré fin février 2015 par email à singhoff@univ-brest.fr
      • Vous devez livrer à la fois le code de votre solution, mais aussi un petit rapport expliquant votre travail et les réponses aux questions ci-dessous.
      • Vous serez aussi évalué lors d'une démonstration organisée fin février.
      • La propreté du code produit et la qualité du rapport seront des éléments importants pour la notation.
      • Enfin, il est strictement interdit d'intégrer un morceau de code que vous n'avez pas écrit.


      Question 1 : test du simulateur

      1. On suppose un jeu de tâches périodiques défini par les paramètres suivants: S1=S2=S3=0, P1=5, C1=1, P2=10, C2=3, P3=30 et C3=8. Calculer l'ordonnancement selon un algorithme preemptif Rate Monotonic. Le jeu de tâches est il ordonnançable ?
      2. Modifiez main.c afin d'exécuter le jeu de tâches ci-dessus, compilez et testez ce programme. Vérifiez que l'ordonnancement généré par ce programme est identique à celui que vous aviez calculé à la main lors de la question précédente.
      3. Dans le fichier main.c et le fichier simulateur.c, trouvez :
        • Où et comment les tâches de ce système sont initialisées.
        • Où et comment les réveils périodiques des tâches sont implantés.


      Question 2 : implantation d'un ordonnancement global préemptif Rate Monotonic pour deux processeurs

      Dans la suite, on regarde maintenant des algorithmes d'ordonnancement global pour des architectures multiprocesseurs.

      On vous demande maintenant de modifier ce simulateur afin de pouvoir supporter un algorithme d'ordonnancement global. L'algorithme global à implanter est Rate Monotonic sur deux processeurs identiques. Vous implanterez deux politiques de migration des tâches :
      1. Migration à tout instant : les tâches peuvent migrer d'un processeur à un autre à tout moment.
      2. Migration de travaux : les tâches ne peuvent migrer qu'en début de réveil périodique. Ici, si la tâche commence le début de son exécution périodique sur un processeur, elle doit terminer cette activation sur ce même processeur.

      1. Vous testerez votre programme avec DEUX jeux de tâches et sur les deux politiques de migration (migration de travaux et migration à tout instant).
      2. Implantez chacun des jeux de tâches dans un fichier main.c différent : en effet, il faudra livrer l'ensemble des fichiers le jour de la démonstration.
      3. Le premier jeu de tâches doit avoir un taux d'occupation de 1 (100% d'un processeur occupé) et le second de 1,8 (ce qui pourrait être interprété comme un premier processeur occupé à 100% et le second à 80%).


      Question 3 : implantation de global EDF et de global LLF et comparaison avec global RM

      Dans cette dernière question, on vous demande d'étendre encore le logiciel de simulation:
      1. Vous compléterez le simulateur avec une implantation de global LLF et de global EDF. La aussi, vous implanterez les deux politiques de migration : migration à tout instant ou migration de travaux.
      2. Vous testerez votre implantation de global EDF et global LLF avec les fichiers de tests de la question 2 .
      3. Calculer pour les six configurations d'ordonnanceur (c'est à dire global RM/EDF/LLF) et de migration (c'est à dire migration de travaux ou à tout moment) les pires temps de réponse de chaque tâche, pour les deux jeux de tests.
      4. Puis, indiquez les configurations d'ordonnanceur et de migration qui sont les plus favorables pour chaque jeux de test. Votre rapport devra comporter une comparaison des résultats (pire temps de réponse) de chaque algorithme/migration pour les deux jeux de tâches.