Académique Documents
Professionnel Documents
Culture Documents
Travaux Pratiques
SOMMAIRE
1. MODE COOPERATIF
SEQUENCEMENT
1
SYSTEMES TEMPS REEL
Travaux Pratiques
PREAMBULE
Cet enseignement est avant tout dédié à la compréhension des services logiciels proposés par
un système d'exploitation, notamment ceux dits temps réel (déterministes). Il devra amener aux portes
de la définition d'une architecture système logicielle répondant à un cahier des charges donné. Les
concepts seront illustrés sur la technologie FreeRTOS, solution open source du marché et leader à
notre époque sur le marché des RTOS (Real Time Operating System) dans l'embarqué .
La trame de TP sur Système Embarqué sera illustrée sur processeur MCU (Micro Controller
Unit) haut de gamme PIC32MZ (PIC32MZ2048EFH144) proposé par Microchip et porté sur le starter
kit, ou maquette de démarrage PIC32MZ EF Starter Kit.
2
SYSTEMES TEMPS REEL
Travaux Pratiques
Installer les outils de développement et venir en séance avec vos machines personnelles (IDE
MPLABX, Framework de développement Harmony et toolchain C XC32). Les TP peuvent être réalisés
indifféremment sur plateforme Windows ou GNU\Linux.
Ouvrir les propriétés du projet : fenêtre Project > clic droit sur le nom du projet > Properties
Configurer le simulateur : Simulator > Options Categories [UART2 IO Options] > Enable
UART2 IO
Ouvrir une console de debug : Window > Debugging > Debug Console
Compiler et exécuter le projet en mode Debug : Debug > Debug Main Project
3
SYSTEMES TEMPS REEL
Travaux Pratiques
1. MODE COOPERATIF
Travail préparatoire
• 2. (1,5pts) Ces états sont-ils les mêmes quelque soit l'OS ou le RTOS utilisé ? Donner un
exemple pour un autre RTOS.
• 3. (0,5pt) Que se passe-t-il sous FreeRTOS lorsqu'aucune tâche, précédemment créée via
xTaskCreate(), ne s'exécute (état running) ?
4
SYSTEMES TEMPS REEL
Travaux Pratiques
1. MODE COOPERATIF
Travail en séance
1.1. Interface de communication
Avant de commencer à découvrir FreeRTOS, un micro-noyau temps réel libre et open source,
nous allons devoir mettre en place une interface de communication avec l'ordinateur de
développement (ordinateur host). Même à notre époque, l'une des premières interfaces mise en
œuvre lors de développement sur MCU est une communication série asynchrone via UART. La rapidité
de mise en œuvre, la simplicité du protocole et sa robustesse en font sa force. Cet exercice ayant déjà
été réalisé en première année à l'école sur architecture PIC18 de Microchip, le programme de
configuration et de gestion de l'UART sur architecture PIC32MZ de Microchip vous est donné. Il vous
est seulement demandé d'en comprendre le fonctionnement tout en réalisant quelques tests après
compilation.
• Créer un projet uart dans le répertoire disco/bsp/uart/pjct/.Ce projet doit inclure les fichiers
sources uart.c et main.c présents dans disco/bsp/uart/src/ et faisant référence au fichier d'en-
tête uart.h présent dans disco/bsp/uart/include/. Sinon, inclure la bibliothèque statique bsplib.a
à la place du fichier uart.c. Il vous sera nécessaire de spécifier à la chaîne de compilation les
chemins suivants afin de permettre au pré-processeur de trouver le headers nécessaires :
▪ C:\<your_path>\disco\bsp
▪ C:\Program Files (x86)\Microchip\harmony\v<your_version>\framework
▪ ou C:\microchip\harmony\v<your_version>\framework
• Pour information, lors de développements sur processeurs pour l'embarqué, nous utilisons des
convertisseurs USB-serial afin de communiquer avec l'ordinateur de développement. Ces
interfaces nécessitent l'installation de drivers sous Windows et sont nativement reconnus sous
Linux en apparaissant à la racine dans le répertoire /dev sous le nom ttyXXXX (teletypewriter
terminal) :
5
SYSTEMES TEMPS REEL
Travaux Pratiques
Nous allons maintenant découvrir pas à pas les principaux services proposés par FreeRTOS.
Dans un premier temps, intéressons-nous au mode coopératif, très peu utilisé pour des problèmes de
robustesse et d'égalité de partage de temps CPU, problèmes que nous illustrerons dès le premier
exercice. En mode coopératif, chaque tâche doit explicitement permettre à une autre tâche de
s'exécuter en effectuant un appel système, sans quoi, aucune autre tâche ne pourra prendre la main.
Nous allons dans un premier temps créer 3 tâches de même priorité. Chaque tâche enverra une chaîne
de caractères à l'ordinateur par liaison série.
• Créer un projet MPLABX coop dans le répertoire disco/apps/coop/pjct. Ce projet doit inclure les
fichiers sources applicatifs disco/bsp/uart/src/uart.c (ou bilbiothèque bsplib.a), ainsi que utask.c
(user tasks), ktrap.c (kernel trap) et main.c présents dans le répertoire disco/apps/coop/src .
Bien observer le contenu des répertoires du projet coop avant de créer le projet.
• Inclure à votre projet les sources de FreeRTOS sans oublier de configurer vos chaînes de
compilation C et ASM avec les chemins vers les différents répertoires contenant des fichiers
d'en-tête. S 'aider de l'annexe 4.
• La documentation du noyau est accessible en ligne sur le site officiel de la société proposant
FreeRTOS :
http://www.freertos.org/
6
SYSTEMES TEMPS REEL
Travaux Pratiques
• Créer 3 tâches de priorité 1. Les fonctions implémentant les tâches se nommeront task1(),
task2() et task3(). Chaque tâche ne fera qu'envoyer une chaîne de caractères à l'ordinateur puis
attendre un délai de quelques centaines de millisecondes via une temporisation logicielle avant
de répéter l'opération. Prenons l'exemple de la tâche 1 :
uart_puts("\r\n1111111");
Les traits pleins apparaissant sur les chronogrammes représentent le code en cours d'exécution
par le CPU. Nous constatons que nous sommes bloqués dans la tâche 3. Du coup, trois questions se
soulèvent :
• Pourquoi sommes-nous bloqués ? tout simplement car en mode coopératif, la tâche doit
appeler elle même l'ordonnanceur ou une fonction système réalisant un appel du
scheduler.
• Dans quel état se trouvent les tâches 1 et 2 ? elles se trouvent à l'état prêt (ready). Elles
sont toutes les deux prêtes à prendre la main si la tâche 3 le leur donne.
7
SYSTEMES TEMPS REEL
Travaux Pratiques
taskYIELD();
Vous constaterez que le noyau donne la main à chaque tâche à tour de rôle. Beaucoup d'OS
temps réel légers travaillent ainsi, il s'agit de la technique dîtes du round-robin qui suit le principe de
fonctionnement d'un tourniquet. Chaque tâches de même priorité prêtes ou en court d'exécution
prendra la main à tour de rôle. Le principal avantage de cette technique est de ne nécessiter qu'une
intelligence très réduite au niveau du code du kernel.
Nous venons d'illustrer le principe de la coopération entre tâches ainsi que le principal
problème amené si une boucle infinie (bug) intervient dans le code d'une tâche. Les tâches bloquées
ou prêtes ne peuvent plus prendre la main et votre application tombe !
http://www.youtube.com/watch?feature=player_embedded&v=WTNc1PwoMG4
8
SYSTEMES TEMPS REEL
Travaux Pratiques
uart_puts("\r\n1111111");
vTaskDelay(3000);
• Quel code s'exécute lorsque nous nous trouvons dans aucune des 3 tâches ?
9
SYSTEMES TEMPS REEL
Travaux Pratiques
Intéressons-nous à la tâche Idle et découvrons comment la détourner afin d'y insérer du code
utilisateur. Par défaut en mode coopératif la tâche Idle ne fait que forcer des commutations de
contexte en appelant la fonction taskYIELD().
• Créer alors une fonction (et non une tâche !) nommée vApplicationIdleHook(). Attention ce
nom est imposé par le système. Cette fonction ne fera qu'envoyer une chaîne de caractères :
/**
* @fn void vApplicationIdleHook( void )
* @brief function called by idle task
*/
void vApplicationIdleHook( void ){
uart_putc('i');
}
• Proposer des cas d'applications et exemples d'utilisation de la tâche Idle. Ne pas hésiter à
s'aider du web et d'exemples de détournement de la tâche Idle sur d'autres noyaux temps réel.
10
SYSTEMES TEMPS REEL
Travaux Pratiques
Travail préparatoire
• 1. (0,5pt) Quels sont les inconvénients et avantages d'un OS coopératif (aidez-vous du Web) ?
• 2. (0,5pt) Quels sont les inconvénients et avantages d'un OS préemptif (aidez-vous du Web) ?
• 4. (1,5pt) Les stratégies de gestion du tas par FreeRTOS sont implémentées dans les fichiers
heap_1.c, heap_2.c, heap_3.c, heap_4.c et heap_5.c présents dans le répertoire
/disco/rtos/FreeRTOS/Source/portable/MemMang de notre arborescence de TP.
• Quelles sont les différences entre les stratégies utilisant heap_1.c ou heap_2.c ?
• Quelles sont les différences entre les stratégies utilisant heap_2.c ou heap_3.c ?
• 6. (1,5pt) Qu'elle est la taille par défaut de la pile de la tâche Idle dans le cadre de notre trame
de TP ? Expliquez votre démarche pour répondre à cette question.
11
SYSTEMES TEMPS REEL
Travaux Pratiques
Travail en séance
2.1. mode préemptif
• Réaliser une copie physique des fichiers du projet coop vers le projet preempt. Créer un projet
MPLABX preempt dans le répertoire disco/apps/preempt/pjct. Ce projet doit inclure les fichiers
sources applicatifs disco/bsp/uart/src/uart.c (ou bibliothèque bsplib.a), ainsi que utask.c (user
tasks), ktrap.c (kernel trap) et main.c présents (après copie) dans le répertoire
disco/apps/preempt/src .
• Inclure à votre projet les sources de FreeRTOS sans oublier de configurer vos chaînes de
compilation C et ASM avec les chemins vers les différents répertoires contenant des fichiers
d'en-tête. S'aider de l'annexe 4.
• Créer 3 tâches, une de priorité 2 et deux de priorité 1. Les fonctions implémentant les tâches
se nommeront respectivement task1(), task2() et task3(). Chaque tâche appellera une fonction
bloquante puis ne fera qu'envoyer une chaîne de caractères à l'ordinateur.
uart_puts("\r\n2222222");
vTaskDelay(1000);
uart_puts("\r\n1111111");
vTaskDelay(3000);
12
SYSTEMES TEMPS REEL
Travaux Pratiques
• Compléter le chronogramme suivant et préciser l'état pris par chaque tâche (R = ready et B =
blocked). Attention aux pièges :
13
SYSTEMES TEMPS REEL
Travaux Pratiques
La partie qui suit est extrêmement importante et sujette à énormément de bugs et mauvais
développement en milieu industriel, notamment lorsque nous travaillons sur de petits exécutifs temps
réel comme FreeRTOS. Problématique différente sur OS évolué (GNU\Linux, Android …) et processeur
avec MMU (Memory Managment Unit). Sur RTOS, le développeur doit avoir une très bonne gestion et
maîtrise des ressources mémoire utilisées par la chaîne de compilation et le système. Prenons un petit
programme d'exemple et observons le mapping mémoire de données du processeur.
int gbl;
/**
* @fn int main(void)
*/
void main(void){
int lclMain;
xTaskCreate(task, "task", 100, NULL, 1, NULL);
vTaskStartScheduler();
}
/**
* @fn void task(void *pvParameters)
*/
void task(void *pvParameters){
int lclTask;
}
14
SYSTEMES TEMPS REEL
Travaux Pratiques
• Nous allons maintenant mettre en place les mécanismes de détection de débordement de pile.
Forcer à 1, 2 ou 3 la macro configCHECK_FOR_STACK_OVERFLOW présente dans le fichier
FreeRTOSConfig.h. En fonction de la valeur choisie différentes stratégies de détection de stack
overflow seront appliquées par le kernel :
http://www.freertos.org/Stacks-and-stack-overflow-checking.html
15
SYSTEMES TEMPS REEL
Travaux Pratiques
• Nous allons maintenant mettre en place les mécanismes de détection de débordement de tas.
Forcer à 1 la macro configUSE_MALLOC_FAILED_HOOK présente dans le fichier
FreeRTOSConfig.h. Observer la fonction vApplicationMallocFailedHook dans le fichier ktrap.c
(kernel trap).
• Éditer le code suivant dans la tâche 1. Nous allons créer une tâche depuis
la tâche 1 dont la pile dépasse la taille du tas. Interpréter et illustrer le
comportement du programme sur le schéma ci-contre :
16
SYSTEMES TEMPS REEL
Travaux Pratiques
Travail préparatoire
void prodTask1( void *pvParam ){ void prodTask2( void *pvParam ){ void consTask( void *pvParam ){
int x=1; int x=2; int y;
char sBuffer[20];
while(1){ while(1){
if (xQueueSend(xQueue, \ if (xQueueSend(xQueue, \ while(1){
&x, 0) == pdTRUE ){ &x, 0) == pdTRUE ){ xQueueReceive(xQueue, &y, \
x += 2; x += 2; portMAX_DELAY );
vTaskDelay( 1 ); vTaskDelay( 1 ); sprintf(sBuffer, ''%d'', y);
} } uart_puts(sBuffer) ;
} } }
} } }
17
SYSTEMES TEMPS REEL
Travaux Pratiques
void task1( void *pvParam ){ void task2( void *pvParam ){ void task3( void *pvParam ){
} } }
} } }
18
SYSTEMES TEMPS REEL
Travaux Pratiques
Travail en séance
3.1. Queue de message
• Réaliser une copie physique des fichiers du projet preempt vers le projet tools. Créer un projet
MPLABX tools dans le répertoire disco/apps/tools/pjct. Ce projet doit inclure les fichiers sources
applicatifs disco/bsp/uart/src/uart.c (ou bibliothèque bsplib.a), ainsi que utask.c (user tasks),
ktrap.c (kernel trap) et main.c présents (après copie) dans le répertoire disco/apps/tools/src .
• Inclure à votre projet les sources de FreeRTOS sans oublier de configurer vos chaînes de
compilation C et ASM avec les chemins vers les différents répertoires contenant des fichiers
d'en-tête. Ne pas hésiter à s'aider des documents d'annexe.
• Créer 3 tâches, la première, périodique de priorité 2 et les deux autres de priorité 1. Les
fonctions implémentant les tâches se nommeront respectivement task1(), task2() et task3().
Chaque tâche ne fera qu'envoyer une chaîne de caractères à l'ordinateur puis se bloquer.
• La tâche 3 (priorité 1)ne fera qu'envoyer un chaîne de caractères côté ordinateur puis se
bloquera durant 1s :
uart_puts("\r\n3333333");
vTaskDelay(1000);
• La tâche 1 (priorité 2) devra être périodique avec un périodicité de 5s. Elle devra
récupérer la valeur courante du tick (sans l'envoyer à l'ordinateur) puis la postera dans
une queue de message pour la tâche 2 (priorité 1). La fonction d'écriture dans la file
d'attente ne devra pas être bloquante.
19
SYSTEMES TEMPS REEL
Travaux Pratiques
• Ne pas oublier de créer un queue de message. A vous de fixer la taille de la file d'attente
ainsi que la taille de chaque élément.
Nous venons ici d'illustrer deux concepts importants dans le domaine des systèmes
d'exploitation, la synchronisation et la communication. La synchronisation permet notamment de
synchroniser l'exécution d'une tâche (par exemple implémentant un traitement long) suite à
l'exécution d'une autre tâche ou d'une ISR (traitement toujours court). La communication inter-tâche
consiste quant-à-elle en la capacité d’effectuer des échanges sécurisés d'informations entre
différentes tâches de l'application. Notion de partage d'information dans des cas d'usage avec
plusieurs écrivains et/ou plusieurs lecteurs. Un troisième concept important, est l'exclusion mutuelle.
20
SYSTEMES TEMPS REEL
Travaux Pratiques
3.2. Timeout
• Forcer le réveil de la tâche 2 toutes les secondes en utilisant le Timeout associé à la fonction
xQueueReceive(). Après avoir testé la nature du réveil de la tâche, envoyer l'une des deux
chaînes de caractères suivantes :
• Que se passe-t-il si nous forçons à portMAX_DELAY le Timeout d'une fonction bloquante (ainsi
que la macro INCLUDE_vTaskSuspend à 1) ? À quelle valeur théorique de timeout correspond
cet argument ?
21
SYSTEMES TEMPS REEL
Travaux Pratiques
Une section critique est une région de code pour laquelle nous devons garantir sa bonne
exécution et l'intégrité des données à sa sortie. Il s'agira le plus souvent de protéger une ressource
partagée (variable globale, accès à un périphérique …). Une section critique peut-être protégée par
différents outils système :
• Sémaphores
• Mutex (mutual exclusion)
• fonctions dédiées le plus souvent par masquage d'interruption
Vous avez normalement dû constater que les tâches 2 et 3 étant de même priorité, elles se
partagent à tour de rôle l'accès à l'UART. Nous allons donc protéger l'accès à ce périphérique. Cela
signifie qu'une tâche ayant pris cette ressource matérielle la gardera jusqu'à-ce qu'elle ait fini le
traitement en cours.
• Pour les tâches 2 et 3, placer la fonction d'envoi de données à l'ordinateur dans une section
critique. Utiliser pour cela les macros taskENTER_CRITICAL() et taskEXIT_CRITICAL().
• Compléter le chronogramme suivant et préciser l'état pris par chaque tâche (R = ready et B =
blocked). Attention aux pièges :
• Cette implémentation des sections critiques par FreeRTOS est assez dangereuse, notamment
dans l'exemple actuellement présenté, vu que les Ticks (générés par timer matériel) ne sont
plus vus de l'ordonnanceur pendant la durée d'exécution de la section (section longue en
temps d'exécution). Quel problème cela peut-il poser ?
Nous découvrirons par la suite une solution à base de sémaphores plus douce et surtout
interruptible par le kernel. Attention, une section critique doit-être la plus courte possible par principe.
Ne pas oublier qu'elle peut potentiellement être partagée avec d'autres tâches voir ISR's. Les solutions
utilisant le principe de masquage d'interruption sont donc à utiliser pour des sections de code très
courtes en temps d'exécution.
22
SYSTEMES TEMPS REEL
Travaux Pratiques
3.4. Sémaphore
Durant l'exercice précédent vous avez été amené à manipuler des sections critiques en utilisant
les fonctions taskENTER_CRITICAL() et taskEXIT_CRITICAL(). Cependant ces deux fonctions sont à
manier avec précaution car une région critique ainsi créée ne peut plus être préemptée par le noyau,
ni par les interruptions matérielles en dessous d'un certain niveau de priorité système (section non
interruptible). Ceci peut donc devenir très dangereux en cas de mauvaise programmation et en
fonction de la criticité de l'application. A l'aide de sémaphores, nous allons créer des sections critiques
pouvant être préempté par le système et également interrompues par les périphériques matériels.
Dans l'exercice qui suit, nous allons réécrire en partie la bibliothèque C de gestion de l'UART et
donc modifier le fichier source uart.c ainsi que le fichier d'en-tête uart.h.
• Créer un sémaphore binaire en choisissant un nom adapté au travail en cours puis modifier le
code source de la fonction uart_puts afin de s'assurer qu'une seule tâche à la fois puisse
prendre en main l'UART en transmission.
• Dans notre cas, est-il plus intéressant d'utiliser un sémaphore binaire ou un mutex ? Justifier
votre réponse puis modifier si nécessaire le programme.
23
SYSTEMES TEMPS REEL
Travaux Pratiques
Nous allons, dans cet ultime exercice, modifier plus profondément les sources de la librairie de
gestion du module UART. Le but étant d'obtenir une bibliothèque optimisée pour travailler avec
FreeRTOS (pas en vitesse d'exécution mais en robustesse et efficacité).
A titre indicatif, beaucoup d'applications font cohabiter bibliothèque réseau (ou stack réseau) et
système d'exploitation. Microchip propose par exemple une librairie réseau libre et open source
indépendante de tout OS, qui n'est donc pas optimisée pour travailler avec notre kernel. FreeRTOS
propose en revanche une librairie réseau implémentant des appels système qui est donc optimisée
pour cohabiter avec le noyau, néanmoins cette stack est un outil propriétaire. Observons rapidement
le coût de certains de ces services en 2014 (gratuit en 2018 depuis le rachat par AMAZON) :
• Modifier l'ISR ainsi que la fonction uart_getc en synchronisant par queue de message les réveils
de la fonction d'interruption avec l'appel de la fonction uart_getc. Chaque caractère reçu sera
posté dans la file d'attente et la fonction de réception de caractères implémentera donc un
appel système bloquant en vidant cette queue de message.
• Une fois ce travail réalisé, modifier le code de la tâche 3 de façon à réceptionner puis renvoyer
des chaînes de caractères envoyées depuis l'ordinateur. S'assurer du bon fonctionnement du
programme. Ultime test, envoyer un fichier texte depuis l'ordinateur et s'assurer de sa bonne
réception et renvoi par l'application embarquée. Le fichier est présent dans le répertoire
disco/bsp/kuart/test/rx-test-file1-2-3.txt. Sous TeraTerm, aller dans Fichier → Envoyer un
fichier...
uart_puts("\r\ndisco# ");
while (1) {
if (uart_gets(str_tmp, APP_BUFFER_SIZE, UART_ECHO)) {
uart_puts(str_tmp);
uart_puts("\r\ndisco# ");
}
}
24
SYSTEMES TEMPS REEL
Travaux Pratiques
25
SYSTEMES TEMPS REEL
Travaux Pratiques
26
SYSTEMES TEMPS REEL
Travaux Pratiques
27
SYSTEMES TEMPS REEL
Travaux Pratiques
28
SYSTEMES TEMPS REEL
Travaux Pratiques
29
SYSTEMES TEMPS REEL
Travaux Pratiques
30
SYSTEMES TEMPS REEL
Travaux Pratiques
31