Travaux Pratiques- Application sur DSP

Transcription

Travaux Pratiques- Application sur DSP
 Travaux Pratiques Logiciel Temps réel Sylvain MONTAGNY sylvain.montagny@univ‐smb.fr Bâtiment chablais, bureau 13 04 79 75 86 86 TP 1 Logiciel Temps réel DSP TMS320C5416 Objectif : Création d’un projet simple, mono‐tâche sur plateforme DSP. Fichier mis à disposition (Moodle Science Bourget) 

Fichier de commande : dsp.cmd Fichier de librairie : rts.lib 1 Création d’un projet
Question 1 :
Créer un nouveau projet « dsp.pjt » dans le répertoire de votre choix. Expliquez : Project Name : ……………………………………………………………………………...…................................................. Location : ……………………………………………………………………………………....................................................... Project Type : ………………………………………………………………………………...………………………………………………. Target :…………………………………………………………………………………………………………………………………………… Après création, expliquez : Dependent Projects : ……………………………………………………………………………………………………………………. Documents : ………………………………………………………………………………………………………………………………… | 1 DSP/BIOS Config :……………………………………………………………………………................................................... Generated Files : …………………………………………………………………………………………………………………………….. Include : ………………………………………………………………………………………………………………………………………….. Librairies : ……………………………………………………………………………………………………………………………………….. Sources : ………………………………………………………………………...………………………………………………………………. Note : Tous les fichiers que vous rajouterez au projet devront se trouver dans votre répertoire de travail. 2 Réalisation du code
2.1 Etape 1
Créer un nouveau fichier source « dsp.c », rentrer le code suivant (qui ne sert à rien !!!), et ajouter le fichier au projet. ( Clic droit sur Sources>Add Files to project>dsp.c ) 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
void main(void) { int a=2,b=3; a=2; b=3; a=b+10; b=a+1; while(1); } Question 2 :
Expliquer les actions de : (Help>Contents) Compile File : ………………………………………………………………………………….................................................... Incremental Build : ……………………………………………………………………………................................................ | 2 Rebuild All : ……………………………………………………………………………………………………………………………………. 2.2 Etape 2
Compiler le projet. Vous allez obtenir le Warning suivant. >>Warning: The project has no cmd file while the Text Linker is selected: Rajouter le fichier « dsp.cmd » Question 3 :
Qu’est‐ce qu’un fichier de commande de façon précise. ………………………………………………………………………………………………………………………………………………………… ………………………………………………………………………..…………………………………………………………………………….. >> warning: entry point symbol _c_int00 undefined (Help>Content : _c_int00) Ce warning est dû au fait que qu’il manque une librairie spécifique au DSP que nous utilisons. En effet, les fonctions C standards ne peuvent pas être connu (printf( ), etc…). De plus, le point d’entrée du programme main( ) doit être précisé. La librairy rts.lib (Run Time Support Library) doit donc être rajouté. >> warning: creating .stack section with default size of 400 (hex) words. Question 4 :
A quelle zone mémoire, ce warning fait il référence ? …………………………………………………………………………………………………........................................................ Spécifier vous‐même sa capacité : Project>Build Options>Linker>Stack Size>0x400. 3 Les différentes configurations du projet :
Faites un affichage sur code assembleur généré (View>Mixed Source/Asm) après avoir compilé dans les deux modes de configuration DEBUG et RELEASE. DEBUG : ………………………………………………………………………………………………………………………………………….. | 3 RELEASE :……………………………………………………..………………………………………………………………………………….. Modifier le code par le code suivant et le compiler en mode DEBUG. 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
#include <stdio.h> void main(void) { int a=2,b=3; a=2; b=3; a=b+10; b=a+1; while(1){ if(a==2){ printf("a=2 !!\n"); } else{ printf("a different de 2\n"); } } } >> warning: creating .sysmem section with default size of 400 (hex) words. Question 5 :
A quelle zone mémoire, ce warning fait il référence ?(Help> Contents>sysmen) …………………………………………………………………………………………………...................................................... Spécifier vous‐même sa capacité : Project>Build Options>Linker>Heap Size>0x400. 4 Ajouter des fichiers au projet
Modifier le code précédent par le code suivant (qui ne sert toujours à rien). | 4 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
void calc(void); void main(void) { int a=2,b=3; a=2; b=3; a=b+10; b=a+1; while(1){ if(a==2){ calc(); } else{ calc(); } } } void calc(void){ int c=0,d=0; c=d+12; d++; d=c+12; } Réaliser un projet multi‐fichiers en déportant la fonction calc() dans un autre fichier. Exécuter le code précédent sur la cible DSP et expliquer les méthodes d’exécution pas à pas « single step » et « step over » dans les deux modes (mode source et mode assembleur). Faites différents essais pour bien comprendre le fonctionnement. Single step : ………………………………………………………………………………………………………………………………… Step Over : ………………………………………………………………………………………………………………………………….. | 5 5 Directives pré-processeur
5.1 #include
Question 6 :
Qu’est ce qu’une directive pré‐processeur ? …………………………………………………………………………………………………......................................................... Aller voir le fichier stdio.h : “C:\CCStudio_v3.1\c5400\cgtools\include”. On retrouve dans ce fichier non pas le code machine des fonctions mais seulement leurs déclarations. Ceci nous permet donc de les utiliser. Au cours de la compilation, les codes machines des fonctions utilisées seront assemblés avec notre fichier compilé pour créer l’exécutable. Il existe deux façons d’inclure un fichier à l’aide la primitive #include #include <xxx.h> : Si le chemin d’accès est connu par le compilateur. #include “c:/…./…xxx.c” : Lorsque vous souhaitez spécifier vous‐même le chemin d’accès. 5.2 #ifdef, #ifndef
Les définitions de symboles peuvent être faites par #define, ou dans Project>Build Option>Preprocessor>Predefine Symbol Afficher le texte « Début du programme » si le symbole PROG est défini. Afficher le texte « Version DEBUG» si le symbole RELEASE n’est pas défini. 5.3 Création de librairies :
Faire les tutorials proposés sur ce sujet. Help>Tutotrial>Code Composer Studio Tutorial>Code Composer Studio IDE>Project Management>Overview Help>Tutotrial>Code Composer Studio Tutorial>Code Composer Studio IDE>Project Management>Creating Files Help>Tutotrial>Code Composer Studio Tutorial>Code Composer Studio IDE>Project Management>Creating a Library project Help>Tutotrial>Code Composer Studio Tutorial>Code Composer Studio IDE>Project Management>Creating a Executable Application | 6 TP 2 Logiciel Temps réel DSP TMS320C5416 Objectif : Utilisation de DSP BIOS, utilisation des fonctions du Board Support Library (BSL) et du Chip Support Library (CSL). Fichier mis à disposition (Moodle) ■
Fichier d’aide CSL : Chip Support Library ■
Fichier d’aide BSL :Board Support Library ■
Fichier de configuration conf_MCBSP ■
Include & libraries: DSK5416.h dsk5416_led.h dsk5416_pcm3002.h dsk5416f.lib 1 DSP/BIOS
Lire l’aide de DSP BIOS sur le chapitre «What is DSP/BIOS ? » Question 1 :
Expliquer ce qu’est DSP/BIOS en précisant en quelques mots les caractéristiques qui vous paraissent les plus pertinentes. …………………………………………………………………………………………………......................................................... …………………………………………………………………………………………………......................................................... | 1 Créer un nouveau projet nommé « dsp1.pjt ». Créer un fichier de configuration (File>New>DSP BIOS Configuration) « dsp1.cdb » et réaliser une simple application « Hello World\n ». Sur la cible DSP (le \n est indispensable pour un printf() ). Si à la compilation, vous avez une erreur : « Project build options do not match Global configuration settings », faites l’opération suivante : Project>Build Options>Compiler>Advance, et cocher « Use Far Calls » dsp1cfg.h est un fichier qui a été généré par le compilateur en prenant en compte la configuration que vous avez saisi dans dsp.cdb (fichier de configuration). Vous pouvez le visualiser (dans include) et vous devez impérativement l’inclure en premier dans votre code source : #include "dsp1cfg.h" Ouvrir le fichier de configuration et faite un tour de tous les modules qui sont répertoriés. Aller aussi voir les fichiers générés (Generated Files) après la compilation. Charger votre code en mémoire : File>Load Program Note : Afin d’associer la compilation et le chargement du code en mémoire, vous pouvez modifier les options de CCS : Option>Customize>Cochez : Load Program after Build Question 2 :
Pourquoi n’a‐t‐on pas eu besoin de fournir de fichier de commande (*.cmd)? ………………………………………………………………………………………………….......................................................... 2 BSL : Board Support Library
Visualiser le fichier d’aide BSL. 1. Utilisation du BSL
Repérer les différents groupes de fonctions disponibles dans l’aide (5416DSK>Board Support Library), et expliquer le rôle du BSL. ………………………………………………………………………………………………….......................................................... ………………………………………………………………………………………………….......................................................... | 2 Faire l’initialisation des périphériques de la carte DSK (Board Setup ) en utilisant la fonction adéquate. A la place de la librairie dsk5416.lib, vous utiliserez la librairie dsk5416f.lib fournie. La carte DSK5416 possède 4 LEDs et 4 switchs, qui permettent à l’utilisateur d’interagir avec le DSP. Réaliser un programme qui fait clignoter une led. Vous réaliserez la temporisation à l’aide d’un compteur (de 0 à 1 000 000, boucle for) pour voir le clignotement. Vous utiliserez les fonctions du BSL pour la gestion des LEDs. 2.1 Création de tâche : TASK
L’objectif de cette partie est de créer 2 tâches différentes exécutant des fonctions du BSL. En réalité, la fonction main() ne sert normalement qu’à initialiser la carte et l’environnement global du DSP. Dès que cela est réalisée, la fonction main() rend la main au noyau et toutes les tâches utilisateurs sont exécutées. A l’aide du fichier de configuration dsp1.cdb, créer une tâche « task0 ». Vous réglerez simplement deux choses : ■
La priorité de cette tâche ■
La fonction C associée à cette tâche (nom de la fonction précédé d’un « _ » / underscore) que vous coderez dans le programme source. Notre fonction s’appellera Blink0( ), donc _Blink0 dans le fichier de configuration. Le code associé à votre tâche sera la suivante : 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
void Blink0() { int delay = 500; // Set delay between LED transitions while( 1 ) { /* Turn the LED on */ DSK5416_LED_on( 0 ); TSK_sleep( delay ); /* Turn the LED off */ DSK5416_LED_off( 0 ); TSK_sleep( delay ); } } Réaliser de la même manière une seconde tache « task1 » permettant de faire clignoter la LED 1 avec un temps de clignotement de 1s. DSP/BIOS «Real time,.Multitask Kernel ». Nous venons de démontrer l’aspect «Multitask Kernel », par la suite, nous nous attacherons à la partie « Real time. | 3 Faire un schéma temporel représentant le temps en abscisse et les tâches qui s’exécutent en ordonnée (main, TSK_idle, task0, task1). Pour chaque tâche vous utiliserez des couleurs différentes suivant les états (que vous préciserez) de celle‐ci. 3 CSL : Chip Support Library
Visualiser le fichier d’aide du CSL Question 3 :
Expliquer le rôle du CSL : ………………………………..………………………..………………………………………........................................................ ………………………………..………………………..……………………………………………………………………………………………. Question 4 :
Quelle est la différence entre le CSL et le BSL ? ………………………………..………………………..………………………………………....................................................... ………………………………..………………………..………………………………………...................................................... Créer un nouveau projet « dsp2.pjt » avec un nouveau fichier de configuration dsp2.cdb. Réaliser la compilation complète de votre projet. 3.1 Analyse du fichier dsp2cfg.h
dsp2cfg.h est un fichier qui a été généré par le compilateur en prenant en compte la configuration que vous avez saisi dans dsp.cdb (fichier de configuration). Certains objets par défaut sont déjà crées, comme par exemple la tâche de fond (TSK_idle) ou le RTDX (module qui permet de dialoguer entre le PC et le DSP). Nous allons rajouter un module de liaison Série McBSP. Pour réaliser une telle manipulation, il faut bien prendre en main toute la liaison série de notre DSP en étudiant sa documentation. Ceci ne rentre pas dans le cadre de ce cours. Nous nous contenterons donc de récupérer une configuration existante. Dans votre répertoire de travail, renommer le fichier de configuration dsp2cfg.h en dsp2cfg_old.h, afin de sauvegarder une trace du fichier pour la suite. Nous allons maintenant modifier la configuration du projet et analyser le nouveau fichier créé. Dans le fichier de configuration CDB fourni (fichier conf_MCBSP.cdb), copier les configurations pour la liaison série : Dans le fichier conf_MCBSP : | 4 Chip Support Libray>MCBSP>MCBSP configuration manager> mcbspCfg0>Clic droit>Copy. Dans votre fichier de configuration : Chip Support Libray>MCBSP>MCBSP configuration manager> mcbspCfg0>Clic droit>Paste. Editer les propriétés du module McBSP2 (MCBSP>MCBSP ressource Manager>McBSP2). Dans ce module, cocher les deux cases et rentrer la configuration suivante : ■
Specify Handle Name : C54XX_DMA_MCBSP_hMcbsp ■
Pre‐initialize : mcbspCfg0 (votre fichier de configuration que vous venez de créer) Remarque : Nous utilisons le MCBSP2, car c’est le numéro 2 qui a été connecté au Codec audio (convertisseur AN /NA : PCM3200). Ainsi l’objectif est de réaliser un dialogue avec ce Codec. Question 5 :
Quelles sont les deux déclarations qui ont été rajoutées au fichier dspcfg.h après compilation de votre projet? …………………………………………………………………………………………………........................................................ Question 6 :
En vous référant à l’aide sur le CSL et plus particulièrement à l’introduction, dire ce qu’est un handle en programmation système ? Handle :…………………………………………………………………………………………………………………………………………. Question 7 :
Quelle est l’effet de l’identificateur « extern » sur la déclaration d’une variable ? Extern : ………………………………………………………………………………………..................................................... Note : Si nous n’avions pas utilisé la configuration statique dans le fichier de configuration de DSP/BIOS, nous aurions pu le faire de façon dynamique, pendant l’exécution du programme avec les fonctions : MCBSP_open() Opens a McBSP port MCBSP_start() Start a transmit and/or receive for a MCBSP port Question 8 :
Grâce à l’aide, expliquer le rôle de la fonction MCBSP_open() et valider le fait qu’elle fasse bien la même chose que ce que nous avons configuré statiquement dans le fichier dsp.cdb. | 5 …………………………………………………………………………………………………........................................................ 4 Réalisation d’une application
En continuant le projet précédent, nous nous proposons de réaliser une application simple mettant en œuvre le MCBSP2 (CSL) et le Codec audio PCM3200 (BSL) fonctionnant à 48 khz (conversion 48 000 fois par seconde). L’objectif est d’envoyer une sinusoïde de 1Khz pendant 5s sur les hauts parleurs. La structure de configuration de la liaison est donnée ci‐dessous (utile lors de l’ouverture du codec audio). Il n’est pas important de comprendre ce contenu. Il sert à configurer l’atténuation des voix droite, gauche, et d’autres éléments peu importants. 1.
2.
3.
4.
5.
6.
DSK5416_PCM3002_Config config = { 0x010d, // Set‐Up Reg 0 ‐ Left channel DAC attenuation 0x010d, // Set‐Up Reg 1 ‐ Right channel DAC attenuation 0x0000, // Set‐Up Reg 2 ‐ Various ctl e.g. power‐down modes 0x0000 // Set‐Up Reg 3 ‐ Codec data format control }; Toute la gestion de cette application sera réalisée dans une tâche dont la fonction sera void UserTask(void). 4.1 Initialisation du tableau de sinus
Afin d’optimiser le temps de calcul sur le processeur et pour simplifier le programme, nous allons initialiser un tableau avec les valeurs du sinus que nous enverrons sur le codec audio (CNA) fonctionnant à 48khz. Question 9 :
Combien faut‐il calculer d’échantillon du sinus afin d’obtenir une fréquence de 1khz. Faite le calcul entier des échantillons d’un sinus d’amplitude 32766, et remplissez un tableau appelé : int sinetable[SINE_TABLE_SIZE]. Attention à la conversion de type entre les variables double de la fonction sinus et votre tableau d’entier. Affichez le graphique de votre sinus (View>Graph>Time/Frequency) à l’adresse sinetable, pour valider cette partie. 4.2 Utilisation du Codec audio
Le codec audio fonctionne à 48khz (valeur par défaut). C'est‐à‐dire qu’il acceptera un nouvel échantillon que lorsque la période de 1/48000 sera écoulée. Le reste du temps il indiquera que la liaison série est « Busy ». | 6 Utiliser les fonctions d’écriture des échantillons de votre sinusoïde au codec audio. Faire une mesure à l’oscilloscope de la sortie du codec audio. Relever l’amplitude et la période du signal. Figure 1 : Schéma du jack audio stéréo 4.3 Ajout de tâches
Remettre dans votre projet les tâches exécutant les clignotements de la LED0 (500 ms) et la LED1 (1 seconde). Visualisez le fonctionnement des LEDs pendant le temps d’écoute de votre son. Question 10 :
Que se passe‐t‐il et pourquoi ? Trouvez une solution à ce problème. | 7 TP 3 Logiciel Temps réel DSP TMS320C5416 Objectifs : Mesure du temps d’exécution des tâches et fonctions 1 Profiling
Reprendre et faire fonctionner le projet du TP précédent. Si cela n’a pas déjà été réalisé, vous ferez une fonction simple (boucle for) qui permet d’initialiser à zéro le tableau de sinus avant le remplissage de celui‐ci : void reset_sinus_tab (void) ; L’objectif est de connaître le nombre de cycles que le processeur met pour exécuter cette initialisation. Pour cela nous allons utiliser une méthode appelée Profing. Cette méthode ne fait pas partie intégrante du noyau DSP/BIOS. L’outil est seulement implémenté dans CCS. Voici l’extrait de la documentation présentant le profiling. When you run a program to generate profile data, resume and halt breakpoints are set at the beginning and end of each profile range. When the program counter (PC) encounters the resume breakpoint, the profiler obtains the value of the profile clock. When the PC encounters the halt breakpoint, the profiler again obtains the value of the profile clock. The profiler uses these two values to calculate the profiling results. Question 1 :
Le programme s’exécutera t‐il en temps réel ? Pourquoi ? ………………………………………………………………………………………………….......................................................... ………………………………………………………………………………………………….......................................................... Compiler votre programme et charger le dans le DSP. | 1 1.1 Configuration du Profiling :
Il faut d’abord désactiver un outil appelé RTDX (Real Time Data eXchange). En effet, cet outil que nous n’étudierons pas, n’est pas compatible avec l’utilisation du Profiling. Tools>RTDX>Configuration Control et désactiver la case « Enable RTDX ». Fermer la fenêtre RTDX : Click droit sur la fenêtre RTDX>Close. Lancer le profiling : Profile > Setup, puis cliquer sur . Cliquer sur Profile all function and Loop for Total Cycles : Cela permet de mesurer le nombre de cycles utilisé pour l’ensemble de l’application. Chaque fonction et chaque boucle sera étudiée. Ouvrir le Profile Viewer : Profile>Viewer Lancer l’application : Debug>Run 1.2 Etude du Profiling
1.2.1 Mesure de l’ensemble du programme Dans le Profile Viewer, sélectionner uniquement la vue des fonctions (bouton ). Visualiser la cohérence des valeurs de access_count correspondant au nombre de fois que sont appelées les fonctions : ■
InitSineTable( ) ■
DSK5416_PCM3002_write16( ) Question 2 :
Donner le nombre de cycle que met la fonction InitSineTable( ) pour s’exécuter : CPU_cycle : incl.total. Note : Après un certain temps le profiling se stop. 1.2.2 Mesure de certaines zones du programme Placez un « Hello World \n » à l’entrée de votre fonction InitSineTable( ). Sélectionner la ligne de votre fonction printf ( ) : Clic‐droit sur la ligne sélectionnée>Profile>Range. Votre ligne s’ajoute au champ Range de l’onglet Range de la fenêtre Profiler. Une icône apparaît à gauche sur la ligne de votre programme. Vous pouvez aussi faire directement un glisser‐déposer une des zones concernées : Range, Function ou Loop de la fenêtre Profiler. Exécuter votre programme. Question 3 :
Noter le nombre de cycle du printf( ). Sachant que le DSP à une fréquence de 160 Mhz, donner le temps d’exécution du printf( ). ………………………………………………………………………………………………………………………………………………………… Comparer le nombre de temps cycle moyen (CPU Cycle : average) pour : ■
La fonction reset_sinus_tab( ) complète >> « function » : …………………………… | 2 ■
La boucle for réalisant l’affectation >> Loop : ………………………………………………. ■
La ligne seule d’affectation à zéro de sinustable[i] >> Range : ………………………. 1.2.3 Réduction des temps de cycle par optimisation Mettre un point d’arrêt sur votre fonction reset_sinus_tab( ). Afficher le code assembleur généré (Clic droit dans l’éditeur>mixed‐mode) et faite du pas à pas (Debug > Assembly/Source Stepping>Assembly Step Into). Vous pouvez aussi utiliser le bouton vert . Ouvrez la visualisation de votre mémoire (View>Memory) à l’adresse de votre tableau de sinus et regarder comment sont affectées à zéro les cases mémoires. Modifier les options d’optimisation de CCS. Project>Build Option>Opt Level > Function –o2. Refaites la même manipulation que précédemment. Question 4 :
tableau? Quelle instruction assembleur performante à été utilisée ici pour l’initialisation du …………………………………………………………………………………………………......................................................... Modifier les options d’optimisation de CCS. Project>Build Option>Opt Level > File –o3. Mettre un point d’arrêt à l’endroit où vous appelez votre fonction reset_sinus_tab( ) et visualiser le code assembleur. Question 5 :
Que remarquez‐vous de spéciale pour l’appel de la fonction ? Comment l’initialisation a‐t‐elle pu se faire ? …………………………………………………………………………………………………......................................................... …………………………………………………………………………………………………......................................................... Faites une mesure de temps de cycle (Profiling) de la fonction printf( ) avec et sans optimisation. Question 6 :
Que remarquez‐vous ? …………………………………………………………………………………………………......................................................... 1.2.4 Réduction par utilisation des outils DSP/BIOS Les options d’optimisation ne donnent aucun résultat au niveau des fonctions telle que printf(). On comprend alors qu’il est absolument indispensable de trouver une alternative à l’utilisation de la fonction C standard pour l’affichage de chaîne de caractère depuis la cible DSP vers le PC. Pour cela nous allons utiliser le LOG module fourni avec les API DSP/BIOS et configurable avec le fichier xxx.cdb de votre projet. En effet, la fonction standard printf( ), réalise elle‐même le formatage des données et l’envoi vers le PC. C'est‐à‐dire que la fonction gère le communication de bout en bout, ce qui en terme de temps d’accès (et donc de cycle machine utilisé) est catastrophique. Dans le cas de l’utilisation du module LOG, la cible DSP se contente d’écrire dans un buffer préalablement déclaré, et le PC hôte va lui‐
même chercher les informations brutes dont il a besoin. | 3 Figure 1 : Transfert de donnée au PC à l'aide du module LOG Créer un nouvel objet LOG appelé trace (Instrumentation>LOG). Inclure std.h (obligatoire pour tout utilisation des modules) et log.h (pour ce module en particulier). Faite un essai de l’utilisation de la fonction LOG_printf( ), Exemple : LOG_printf(&trace, “hello world”); Question 7 :
Faite une mesure du temps d’exécution de la fonction LOG_printf( ) et comparer avec la fonction printf( ). Nous ne pouvons pas voir le message pour l’instant car la fonction LOG_prinf( ) utilise le module RTDX (inhibé précédemment). …………………………………………………………………………………………………......................................................... …………………………………………………………………………………………………......................................................... Réactiver RTDX : Tools>RTDX>Configuration Control et activer la case « Enable RTDX ». Le message « hello world » apparaît dans le « Message Log »: DSP/BIOS>Message Log. Si votre application utilise 100% du temps processeur (si par exemple vous êtres dans une boucle infinie en scrutation), il n’est pas possible à DSP/BIOS d’avoir le temps de transmettre votre message. Il est donc utile de forcer le passage en utilisant la fonction IDL_run( ) dans votre tâche UserTask( ). Relancer l’application. | 4 TP 4 Logiciel Temps réel DSP TMS320C5416 Objectifs : Visualiser l’ordonnancement des différentes tâches d’une application. Fichier mis à disposition (Moodle Bourget ) : ■
Datasheet TMS320C54x_Archi_vol1 ■
DSP BIOS User Guide ■
Fichier de l’application annulation d’écho 1
Etude du main ( ) et de la boucle IDL ( )
Réaliser un projet idle.pjt Vous utiliserez le code suivant afin de mettre en évidence la façon dont le système gère les différentes tâches qu’il exécute. 1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
#include "idlecfg.h" // Mettre le nom correspondent à votre projet #include <std.h> #include <log.h> /* * ======== main ======== */ Void main() { LOG_printf(&trace, "hello world!"); /* fall into DSP/BIOS idle loop */ return; } | 1 Après la séquence de démarrage et l’exécution de la fonction main( ), une application DSP/BIOS tombe dans une tâche de fond appelé idle loop. Cette tâche s’exécute jusqu'à que vous arrêtiez votre programme. Cette tâche est la moins prioritaire de votre application. C’est dans cette partie que se déroule les instructions de l’ordonnanceur et les transferts d’information vers le PC. Dans notre cas, cette tâche s’exécute toute seule puisque nous n’avons pas défini d’autre processus à exécuter. Cependant, elle pourrait être préemptée par des interruptions hardware, ou software. Charger votre code en mémoire du DSP et mettre un point d’arrêt sur la ligne du LOG_printf. Mettre un autre point d’arrêt sur l’adresse IDL_F_loop (Debug>Breakpoints> taper l’adresse IDL_F_loop). IDL_F_loop correspond à la fonction exécutée par le la tâche IDL. Au lancement du programme, le CPU s’arrête sur le premier point d’arrêt. Question 1 :
A l’aide de la documentation du DSP fournie, donner le rôle du bit INTM ? ………………………………………………………………………………………………….......................................................... Question 2 :
Quelle est sa valeur (View>Registers>CPU Registers) ? ………………………………………………………………………………………………….......................................................... Relancer le programme, le CPU s’arrête sur le prochain point d’arrêt. Question 3 :
Quel est la nouvelle valeur du bit INTM (View>Registers>CPU Registers)? Quelle source d’interruption peut nous interrompre ? ………………………………………………………………………………………………….......................................................... Question 4 :
Que se passe‐t‐il si on relance notre application encore et encore ? Est‐ce logique d’après l’application que nous avons réalisée ci‐dessus ? ………………………………………………………………………………………………….......................................................... ………………………………………………………………………………………………….......................................................... 2 Ordonnancement
Nous allons voir comment réaliser une application contenant des tâches de même priorité. Ces tâches auront toujours une priorité plus faible que les interruptions hard (HWI) ou soft (SWI). La différence principale entre les tâches TSK et les interruptions matérielles (HWI) et logiciel (SWI), est qu’une tâche TSK peut bloquer sa propre exécution pour attendre un certain temps, ou simplement libérer le processeur par soucis d’équité envers les tâches de même priorité qu’elle. Essayez d’estimer le résultat de l’application suivante parmi les propositions qui vous sont faîtes Tableau 2? Vérifier ensuite votre réponse en lançant l’application sur le DSP. | 2 Créer un projet DSP/BIOS nommé test_task.pjt dont le fichier source est ci‐dessous. Créer trois nouvelles tâches. Chacune des tâches exécutera la fonction task( ). Object Name Task function task0 task1 task2 15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
Task function argument 0 _task 0 _task 1 _task 2 Tableau 1 : Liste des tâches #include "test_taskcfg.h" #include <std.h> #include <log.h> #include <tsk.h> #define NLOOPS 5 void task(Arg id_arg); /* Function for tasks created with Config Tool */ /* ======== main ======== */ Void main() { } /* ======== task ======== */ Void task(Arg id_arg) { Int id = ArgToInt (id_arg); Int i; for (i = 0; i < NLOOPS ; i++) { LOG_printf(&trace, "Loop %d: Task %d Working", i, id); TSK_yield(); } LOG_printf(&trace, "Task %d DONE", id); } Réponse A Loop 0: Task 0 Working Loop 1: Task 0 Working Loop 2: Task 0 Working Loop 3: Task 0 Working Loop 4: Task 0 Working Task 0 DONE Loop 0: Task 1 Working Loop 1: Task 1 Working Loop 2: Task 1 Working Loop 3: Task 1 Working Réponse B Loop 0: Task 0 Working Loop 0: Task 1 Working Loop 0: Task 2 Working Loop 1: Task 0 Working Loop 1: Task 1 Working Loop 1: Task 2 Working Loop 2: Task 0 Working Loop 2: Task 1 Working Loop 2: Task 2 Working Loop 3: Task 0 Working Réponse C Loop 0: Task 0 Working Loop 1: Task 1 Working Loop 2: Task 2 Working Task 0 DONE Task 1 DONE Task 2 DONE | 3 Loop 4: Task 1 Working Task 1 DONE Loop 0: Task 2 Working Loop 1: Task 2 Working Loop 2: Task 2 Working Loop 3: Task 2 Working Loop 4: Task 2 Working Task 2 DONE Loop 3: Task 1 Working
Loop 3: Task 2 Working Loop 4: Task 0 Working Loop 4: Task 1 Working Loop 4: Task 2 Working Task 0 DONE Task 1 DONE Task 2 DONE Tableau 2 : Propositions du résultat de l'application ■
Réponse A : La tâche 0 exécute la fonction task( ) entièrement, puis la tâche 1 exécute la fonction task ( ) entièrement, puis la tâche 2 exécute la fonction task ( ) entièrement. ■
Réponse B : Le programme affiche les 3 messages des 3 tâches pour la boucle 0, puis les 3 messages pour la boucle 1, etc … ■
Réponse C: Le programme affiche un message pour la boucle 0 de la tâche 0, puis de la boucle 1 de la tâche 1, puis de la boucle 2 de la tâche 2. Visualiser le graphique d’exécution des taches : DSP/BIOS>Execution Graph. Puis relancer l’application. Afin de visualiser les données transmises par le DSP vers le PC, la cible DSP utilise un autre objet LOG en plus de celui que vous avez créé pour le LOG_printf( ) (trace). Il s’agit du LOG_system. Il est déjà implémenté dans le fichier de configuration. Modifier les propriétés de cette objet log : LOG>LOG_system>Property (buflen : 512, logtype : fixed). Question 5 :
A quoi correspond la tâche KNL_swi ? Quel est son rôle ? (Help>Content) ………………………………………………………………………………………………….......................................................... Ouvrir le fichier de configuration et incrémenter la priorité d’une des tâches. Avant de lancer l’application, essayer d’estimer le nouveau diagramme d’exécution des tâches. Une tâche qui vient d’être préemptée par une autre tâche de priorité plus importante est positionnée en tête de la liste d’attente. Donner le diagramme d’exécution des tâches de l’affirmation précédente, en supposant que vous ayez donné une priorité haute à la tâche 2, que la tâche 0 est en cours d’exécution et que la tâche 1 est prête au moment de la préemption. | 4 3 Horodatage des tâches
Etudier le chapitre 4.8 de DSP/BIOS User Guide. Question 6 :
A quoi correspond le « high resolution time » ? Sachant que sa valeur est codée sur 32 bits, donner la valeur absolue du temps le plus long que nous pouvons mesurer en « high resolution time ». ………………………………………………………………………………………………….......................................................... Question 7 :
A quoi correspond le « low resolution time » ? Sachant que sa valeur est codée sur 32 bits, donner la valeur absolue du temps le plus long que nous pouvons mesurer en « low resolution time ». ………………………………………………………………………………………………….......................................................... Question 8 :
Comment fait‐on pour configurer le « low resolution time » dans le fichier de configuration ? ………………………………………………………………………………………………….......................................................... ………………………………………………………………………………………………….......................................................... Question 9 :
Quelle fonction faut‐il utiliser dans le module CLK pour récupérer les valeurs du « low » et « high resolution time » ? ………………………………………………………………………………………………….......................................................... ………………………………………………………………………………………………….......................................................... Compléter le projet précédent avec NLOOPS=3, pour évaluer les horaires où les tâches s’exécutent. Vous afficherez les horaires dans le message LOG (à l’aide de LOG_printf() ) de la façon suivante : ■
Low resolution time : xxxx ticks, Task x ■
High resolution time : xxxx Note 1 : La fonction LOG_printf( ) accepte au maximum deux variables paramétrables (par exemple deux %d ). Note 2 : Pour faire l’affichage de valeur sur 32 bits (long int), nous devons effectuer l’affichage en deux temps, d’abord le poids faible, puis ensuite le poids fort. Note 3 : Il peut être utile d’augmenter la taille du buffer trace. Objet trace>Property>bufflen : 512. Pour cet horodatage des tâches, vous devriez avoir un low resolution time de 0. En effet, l’exécution de l’ensemble des boucles pour toutes les tâches se réalise en moins de 1 tick. | 5 Modifier votre application à l’aide d’un TSK_sleep (exemple : TSK_sleep(1500) ; ) afin de visualiser un horodatage des tâches sur une plus longue période. Pour les plus avancées (Optionnel): Reprendre l’application précédente en ne créant plus les tâches de façon statique (avec le fichier de configuration) mais en les créant dynamiquement avec les fonctions appropriées : TSK_create( ), etc… Lire Paragraphe 2.2.4 de la documentation DSP/BIOS User Guide dans un premier temps. 4 Echo Canceler
4.1 Présentation
Cette application est un exemple de système temps réel donné dans les tutorials de DSP/BIOS, on se propose ici de reprendre la problématique. Le système d’annulation d’écho est composé des parties suivantes : ■
Un décodeur, qui compresse les données reçues en PCM (Pulse Code Modulation) ■
Un encodeur qui retransmet les données après l’annulation d’écho. ■
Une annulation d’écho. Dans la figure suivante, nous retrouvons les différents blocs. Les temps et le nombre d’instruction sont donnés approximativement. Nous n’expliquons pas ici pourquoi le cahier des charges impose une période de 2,5 ms pour la fonction d’annulation d’écho et 22,5 ms pour les fonctions du décodeur et de l’encodeur. Afin de se focaliser sur l’ordonnancement des tâches sans la complexité de l’encodeur, du décodeur et de l’algorithme d’annulation d’écho, nous allons travailler avec des fonctions périodiques qui se déclenchent grâce au timer du DSP toutes les 2,5ms ou 22,5 ms (suivant la fonction). De plus, la charge processeur des trois tâches sera simulée par une boucle qui consomme du temps CPU. Cette charge sera configurable en cours d’utilisation de votre application par CCS. | 6 Dans DSP/BIOS, il existe 4 types de threads principaux (HWI, SWI, TSK et IDL). De plus, 2 autres types de fonctions existent. Il s’agit des fonctions CLK et PRD. Dessiner une échelle des priorités en plaçant les 4 types de thread qui existent, puis rajouter les deux types de fonctions CLK et PRD d’après la documentation proposé ci‐dessous. Il est bien préciser ici que toutes les fonctions PRD ont la même priorité. Cette priorité est définie par la priorité de PRD_SWI. 4.2 Le code de l’application
■
Fichier echo.h : Défini les constantes du programme ■
Fonction main : Ne fait rien ■
Fonction canceler : Cette fonction récupère les pointeurs sur la prochaine trame utilisée. Cette fonction lance aussi « cancelerAlg » qui simule la charge processeur de l’algorithme d’annulation d’écho. ■
Fonction cancelerAlg : Simulation de la charge processeur pour l’annulation d’écho. ■
Fonction encoder : Cette fonction récupère les pointeurs sur la prochaine trame utilisée. Cette fonction lance aussi « encoderAlg » qui simule la charge processeur de l’algorithme d’encodage. ■
Fonction encoderAlg : Simulation de la charge processeur pour l’encodage. ■
Fonction decoder : Cette fonction récupère les pointeurs sur la prochaine trame utilisée. Cette fonction lance aussi « decoderAlg » qui simule la charge processeur de l’algorithme d’encodage. ■
Fonction decoderAlg : Simulation de la charge processeur pour le décodage Créer un projet DSP/BIOS nommé echo.pjt. Intégrer l’ensemble des fichiers sources dans votre projet. 4.2.1 Mise à jour de l’unité de temps (ticks) Nous allons changer la valeur de « low resolution time » du DSP, afin que l’unité de mesure nommé ticks représente un temps de 2500 ms, au lieu des 1000 ms seconde (par default) : | 7 Fichier de configuration > Propriétés de CLK‐Clock Manager> Microseconde / Interruption = 2500. Noter que le registre PRD du timer intégré au DSP change automatiquement. La nouvelle valeur dépend de la vitesse de cadencement du DSP rentrée dans les paramètres (System>Global Settings) qui est ici de 160 Mhz. 4.2.2 Création des objets PRD Les objets PRD servent à exécuter des tâches de façon régulière dès lors qu’une interruption du timer survient. Nous allons donc utiliser ce type d’objet pour lancer les tâches de l’encodeur, du décodeur et de l’annulation d’écho. Créer trois PRD Object avec les noms suivants et les propriétés suivantes. Compléter le champ « resulting period (ms) » du tableau suivant : Object Name period (ticks) function Resulting period (ms) cancelerPrd 1 _canceler encoderPrd decoderPrd 9 9 _encoder _decoder Modifier les propriétés du LOG_system (buflen : 512, logtype : circular). 4.2.3 Visualisation de l’exécution des tâches Lancer l’application et visualiser : ■
Le diagramme d’ordonnancement des tâches : DSP/BIOS> Execution graph ■
La charge du CPU : DSP/BIOS>Load Graph ■
Les statistiques sur les objets créés : DSP/BIOS>Statistique view ■
La fenêtre de sortie des Message Log : DSP/BIOS>Message Log ■
Visualiser la variable decoderLoad Clic droit> Add to watch window : 4.2.4 Etude du graphe d’exécution : Question 10 : Combien de fois est exécuté CancelerPrd pendant une exécution de encoderPrd et decoderPrd ? ………………………………………………………………………………………………….......................................................... Question 11 :
Quelle est la charge CPU ? ………………………………………………………………………………………………….......................................................... Question 12 : Le système fonctionne‐t‐il en temps réel en rapport au cahier des charges que nous nous sommes fixé? ………………………………………………………………………………………………….......................................................... | 8 4.2.5 Etude des statistiques : Le « count field » correspond au nombre de fois ou les objets sont exécutés. Les champs total, Max et Average restent à zéro. En effet, l’unité est le tick pour les PRD Object (contrairement au SWI Object dont l’unité est le cycle instruction). Sachant que les fonctions s’exécutent en moins de 1 tick, elles restent donc à zéro. A noter que nous n’avons pas encore augmenté la charge des fonctions. (variable decoderLoad, encoderLoad et cancelerLoad…) 4.2.6 Augmentation de la charge des tâches Dans la « watch window », mettre la valeur de decoderLoad à 1000. Cela provoque une exécution de 1000 x decoderLoad instruction dans la tâche du décodeur, soit 1 000 000 cycles instructions. Question 13 : Quel est la charge CPU ? Le système fonctionne‐t‐il encore en temps réel ? Expliquer comment il peut être possible de perdre l’aspect temps réel d’une application et de ne pas utiliser toutes les ressources du processeur. ………………………………………………………………………………………………….......................................................... Question 14 : Quelle est la valeur maximale de decoderLoad que vous pouvez mettre afin que votre processeur fonctionne en temps réel ? ………………………………………………………………………………………………….......................................................... Question 15 : Quelle est la valeur maximale de decoderload que vous pouvez mettre afin que votre processeur fonctionne à 100% de ces capacités CPU ? Que se passe‐t‐il pour l’affichage des « message Log » lorsque votre processeur a atteint les 100% ? En déduire la priorité de la tâche gérant le transfert des données de la cible DSP vers le PC. ………………………………………………………………………………………………….......................................................... ………………………………………………………………………………………………….......................................................... Question 16 :
Quand sont gérées les tâches qui ont manquées leurs deadline ? ………………………………………………………………………………………………….......................................................... Question 17 :
Que faudrait‐il changer afin de solutionner ce problème. ………………………………………………………………………………………………….......................................................... 4.2.7 1ère correction de l’ordonnancement Nous allons maintenant utiliser une tâche de type SWI pour l’annulation d’écho. En effet, dans l’exemple précédent, les trois tâches s’exécutent avec la même priorité. De ce fait lorsqu’une tâche possède la ressource processeur, elle empêche les autres de l’obtenir, ce qui provoque le manquement d’une deadline. Nous allons donc toujours utiliser des tâches PRD pour encoder et decoder. En revanche, pour le « canceler » (qui est la tâche la plus critique en terme de temps), nous allons utiliser aussi une tâche PRD mais qui fera l’appel à un objet SWI [SWI_post( )] . | 9 Modifier les propriétés de cancelerPRD : ■
Function = _SWI_post ■
arg0 = cancelerSwi Créer un objet SWI ■
Name = cancelerSwi ■
Function = _canceler ■
Priority = 1 Question 18 :
A quoi sert la fonction PRD_swi ? (Help>Content) ………………………………………………………………………………………………….......................................................... Analyse de l’exécution : Le module PRD pilote les fonctions périodiques de la façon suivante : The PRD_swi SWI is posted once for a PRD tick. PRD_swi loops through PRD objects. If it is time for a PRD object to run : Run that PRD object’s function. PRD_swi continues loop of all PRD objects. Renseigner les différents scénarios suivants, vous devez être capable d’établir la cohérence de l’exécution qui se produit. On rappelle que la priorité de la fonction PRD_SWI modifie la priorité de toutes les fonctions PRD. « decoderLoad » sera remis à zéro dans un premier temps. Scénario Fonctions Priorité PRD_swi 1 1 Canceler_Swi 1 PRD_swi 2 2 3 Déroulement de l’application Canceler_Swi 1 PRD_swi 1 | 10 Canceler_Swi 2 Remettre la charge du decoderLoad à 1000. Question 19 :
Quel est la charge CPU ? Le système fonctionne‐t‐il encore en temps réel ? ………………………………………………………………………………………………….......................................................... Avant de lire les explications suivantes, analyser le comportement de l’application et noter l’endroit où le temps réel n’est pas respecté. Le problème est que toutes les tâches PRD s’exécutent avec le même niveau de priorité. Lorsque l’objet PRD de l’annulation d’écho souhaite poster son interruption SWI, la fonction décodeur est en train de tourner et ne laisse pas la main à cancelerPrd. En effet, même si la fonction cancelerSwi possède une priorité très élevé, elle ne pourra pas s’exécuter puisque l’objet PRD cancelerPrd n’aura pas pu poster l’interruption software [ SWI_post(cancelerSwi) ]. Nous devons donc trouver un moyen d’établir des priorités entre les différentes fonctions PRD. Conclusion : Les actions réalisées au sein d’une fonction PRD doivent obligatoirement mettre moins de 1 tick pour s’exécuter (puisque dans notre cas la fréquence de cancelerPrd est de 1 tick). Dans le cas contraire, il faut passer par des SWI afin de spécifier des priorités entres les tâches et ainsi permettre à ce qu’elle puisse s’interrompre entres elles. 4.2.8 2ème correction de l’ordonnancement Rajouter les deux objets SWI suivant : Object Name function encoderSwi _encoder decoderSwi _decoder Modifier les objets PRD encoderPrd et decoderPrd de la façon suivante (même procédure que précédemment pour le cas de cancelerPrd). Object Name period (ticks) function arg0 encoderPrd 9 _SWI_post encoderSwi decoderPrd 9 _SWI_post decoderSwi Modifier les priorités des objets SWI de la façon suivante. | 11 Lorsque vous utilisez un nouveau niveau de priorité, vous pouvez avoir un message d’erreur suivant : "System stack size (see MEM) is too small to support a new SWI priority level.". En effet, un niveau de priorité supplémentaire amène le système à sauvegarder un contexte d’environnement supplémentaire lors d’un changement de tâche. Il faut donc que vous augmentiez la valeur de la stack système de votre application : System>MEM (Memory Section Manager)>clic droit (Property)> Stack Size : 0x400 Question 20 :
Analyser le système avec decoderLoad = 0, puis avec decoderLaod = 1000. ………………………………………………………………………………………………….......................................................... Question 21 : Augmenter la charge du decoderLoad jusqu'à arriver à perdre l’aspect temps réel de l’application. Quelle est la charge du processeur pour laquelle l’application n’est plus temps réel. Conclure ………………………………………………………………………………………………….......................................................... | 12 TP 5 Logiciel Temps réel DSP TMS320C5416 Objectif : Réaliser une queue de message. Comprendre et utiliser des sémaphores pour synchroniser des tâches sur l’accès à une ressource partagée (queue dans notre cas). 1 Réalisation d’une queue de message
1.1 Présentation
Les queues de messages sont des listes doublement chaînées de type FIFO (First In First Out). Leur taille n’est pas limitée. Les éléments qui la composent sont des structures dont le format est en partie imposé : typedef struct MsgObj { QUE_Elem elem; ... } MsgObj; // Partie imposée // Champs choisis par l’utilisateur Le premier élément n’est pas modifiable et est de type QUE_Elem dont le format est le suivant : typedef struct QUE_Elem { struct QUE_Elem *next; struct QUE_Elem *prev; } QUE_Elem Il s’agit des pointeurs sur l’élément suivant et du pointeur sur l’élément précédent. Cela permet de réaliser une liste doublement chaînée. Question 1 :
Rappeler le fonctionnement d’une mémoire de type FIFO en la schématisant. 1.2 Application
Nous nous proposons de réaliser une application d’écriture et de lecture dans une queue de message. Le seul but est de vérifier le fonctionnement et l’utilisation de l’objet Queue. Nous souhaitons donc obtenir le résultat suivant sur le Message Log. | 1 Figure 1 : Objectif de l'application de test d'une queue de message 1.3 Réalisation
1. 2. 3. 4. Créer un projet queue.pjt. Insérer une nouvelle tache qui exécutera une fonction : void reader_writer(void) Insérer un objet QUE dans le fichier de configuration. Définissez votre structure de message qui ne contiendra qu’un caractère: typedef struct MsgObj { QUE_Elem elem; // champ imposé char val; // champ caractère pour l’ } MsgObj; 5. Vider la queue de message à l’aide la fonction QUE_new(….) (facultatif) 6. Utiliser la fonction MEM_alloc(0,sizeof(MsgObj),0), pour allouer de la mémoire à une nouvelle structure MsgObj qui nous allons insérer dans la queue. 7. Mettre ce message dans la queue avec la fonction QUE_put(……,……) 8. Afficher la valeur du caractère envoyé dans la queue : LOG_printf( ) 9. Récupérer le message de la queue avec la fonction QUE_get(……) 10. Affichez la valeur du caractère récupérer : LOG_printf( ) 11. Libérer la mémoire allouée pour la structure de message : MEM_free(0,……, sizeof(MsgObj)) Vous pouvez alors réaliser votre application en stockant 4 messages à la suite (a,b,c,d), puis en lisant les messages (a,b,c,d). 1.4 Etude de la liste doublement chaînée
Partie optionnelle à réaliser pour les étudiants les plus avancés seulement. Pour les autres, vous pouvez passer directement au chapitre « Synchronisation par Sémaphore » Mettre des points d’arrêt dans votre programme afin de visualiser l’adresse des structures de message lors de l’allocation dynamique d’une part (après MEM_alloc) puis lors de la récupération des messages de la queue (après QUE_get() ). Noter les adresses dans le tableau suivant, puis valider le fonctionnement : Numéro message
1
2
3
4
@ structure message écrit
@ structure message lu
Question 2 :
Quelle est le nom générique de la zone mémoire ou l’allocation dynamique est réalisée ? ………………………………………………………………………………………………….......................................................... | 2 Visualiser les segments mémoire que vous avez à votre disposition dans le fichier de configuration : System>MEM Question 3 :
En les parcourant un par un, nommé le segment où la zone d’allocation dynamique est réalisée. Quelle est la taille réservée pour l’allocation dynamique ? ………………………………………………………………………………………………….......................................................... Question 4 :
D’après l’adresse de la première structure de message qui a été réservé en mémoire, pouvez‐vous estimer l’adresse de début de la zone d’allocation dynamique. Donner l’adresse de fin sachant que l’utilisation de l’allocation dynamique se fait en décrémentant les adresses. Est ce cohérent ? ………………………………………………………………………………………………….......................................................... ………………………………………………………………………………………………….......................................................... Question 5 :
Réaliser un schéma représentant toutes les données que vous avez relevé : ■
Nom du segment où se trouve le heap, @ de début, @ de fin ■
Taille du heap, @ de début, @ de fin | 3 Visualiser la mémoire à l’adresse du début de votre heap. Relancer votre programme, et remplir « dynamiquement sur votre feuille » (avec un crayon de papier !!!) les zones mémoire lors de chaque allocation dynamique des 3 premiers messages. Vérifier le fonctionnement des listes doublement chaînées. @ mémoire
Contenu HEX de la mémoire
@ de la 1ère structure de message
Champs
…
…
…
…
…
*next
…
…
*next
*prev
char
Nous allons modifier la taille du tas à 0x0C MADU (Memory Adressable Data unit) : System>MEM>IDATA>clic droit Property>Heap Size : 0x0C Relancer votre application et ouvrir l’outil de visualisation des Objets présents en mémoire : DSP/BIOS>Kernel/Object View Nous allons nous intéresser à la partie MEM Noter les valeurs et évaluer la cohérence de : ■
Start Address :…………………………………………………………………………... ■
End Address :…………………………………………………………………………… ■
Total size :………………………………………………………………………………. ■
Free Mem :……………………………………………………………………………… Lancer votre programme avec les points d’arrêts et visualiser la place restante au fur et à mesure des allocations réalisées. Question 6 :
Que se passe‐t‐il lorsque le système dépasse l’espace mémoire disponible. ………………………………………………………………………………………………….......................................................... 2 Synchronisation par sémaphore
2.1 Présentation des sémaphores :
Un sémaphore est une structure de données possédant un compteur spécifiant le nombre de tâches concurrentes pouvant utiliser la ressource. Lorsque la ressource n’est pas disponible, une file d’attente se crée. DSP/BIOS intègre des objets spécifiques de type SEM qui permettent de gérer ce type de sémaphore à compteur. Pour poster un sémaphore (vendre, opération V), on implémente | 4 la fonction SEM_post ( ). Cette action incrémente le compteur du sémaphore. Pour prendre un sémaphore, on implémente la fonction SEM_pend( ). Si le compteur est supérieur à zéro, la tâche décrémente simplement le compteur. Si le compteur est égale à zéro, la tâche attends qu’un SEM_post( ) soit réalisé sur le sémaphore (par une autre tâche). Pendant ce temps d’attente, la ressource processeur est utilisée pour d’autres tâches. Le paramètre timeout de SEM_pend( ) permet à une tâche d’attendre un temps maximum spécifier par timeout, d’attendre indéfiniment (SYS_FOREVER) ou de ne pas attendre du tout (0). Lorsque plusieurs tâches attendent sur un sémaphore, la première tâche arrivée sur le sémaphore accède à la ressource lorsqu’elle se libère. Cette tâche n’est pas nécessairement la tâche de plus forte priorité. Question 7 :
Retrouver dans le diagramme d’état des tâches dans quel état se trouve une tâche lorsque qu’elle attend sur un sémaphore ? ………………………………………………………………………………………………….......................................................... 2.2 Présentation de l’application
Nous nous proposons de réaliser une application lecteur/écrivains. Des écrivains inscrivent des données dans une queue de message, alors qu’un lecteur les lit. Contrairement à l’application précédente, les tâches des écrivains et celle du lecteur sont toutes en concurrences les unes par rapport aux autres. Ainsi, il est indispensable de les synchroniser. En effet, l’application ne doit pas accepter qu’un lecteur lise une queue vide. D’une manière générale, le compteur du sémaphore est initialisé avec le nombre de ressources disponibles. Dans notre cas, il est initialisé à zéro puisque qu’il faudra qu’un écrivain inscrive un message dans la QUEUE avant que le lecteur puisse le récupérer. Il faut donc bloquer le lecteur sur le sémaphore. 2.3 Réalisation
En reprenant le projet précédent, modifier votre fichier de configuration en créant 5 tâches avec les caractéristiques suivantes : Object Name Priority Task function Task function argument 0 initTsk 15 _initTask 0 reader0 2 _reader 0 writer0 1 _writer 0 writer1 1 _writer 1 writer2 1 _writer 2 Changer les propriétés du LOG _system : Instrumentation>LOG>LOG_system : buflen : 512, buftype : fixed Remplacer le fichier.c de votre projet par celui qui vous est fourni : semaphore.c Nous allons utiliser un sémaphore pour synchroniser les tâches d’écriture et la tâche de lecture dans la queue de message. Créer un sémaphore : Synchronization>SEM | 5 Question 8 :
Quelles fonctions allez‐vous utiliser pour gérer le sémaphore ? ………………………………………………………………………………………………….......................................................... ………………………………………………………………………………………………….......................................................... Question 9 :
Afin de lancer votre programme, estimer ce qu’il va être écrit dans le « message Log ». Vérifier en exécutant votre programme. Vérifier aussi en utilisant le graphe d’exécution (DSP/BIOS> Exécution Graph) L’ordre d’apparition des tâches « writer » dépendent exclusivement de l’ordre dans lesquelles vous les avez créées dans le fichier de configuration. Pour les plus avancées : Positionner un TSK_yield( ) dans la tâche écrivain juste après avoir posté le sémaphore. Question 10 :
Comment votre programme va‐t‐il réagir, vérifier en l’exécutant sur la cible ? ………………………………………………………………………………………………….......................................................... Modifier les priorités des tâches et tester. | 6